Using TypeScript to categorize items based on common characteristics and assigning them a unique group ID

I am looking to create a function that can achieve the following:

  • Accepts an array of products as input
  • Returns a new array of products with a unique groupId attribute for each
  • Products will share the same groupId if they have common attributes specified by the groupIfIdentical parameter

Here is the basic structure of the function:

    function groupProductsBySharedAttributes(
      products: Product[],
      groupIfIdentical: (keyof Product)[],
      initialGroupId: string,
    ) {
      
    }

Products with matching attributes should be grouped in this way:

export function productsAreIdentical(
  prod1: Product,
  prod2: Product,
  groupIfIdentical: (keyof Product)[],
) {
  for (const key of groupIfIdentical) {
    if (prod1[key] !== prod2[key]) {
      return false
    }
    return true
  }
}

For example:

const prods = [{
  country: 'china',
  material: 'steel',
  sku: 1453
},
{
  country: 'china',
  material: 'steel',
  sku: 4874
},
{
  country: 'japan',
  material: 'steel',
  sku: 4874
},
]


const result = groupProductsBySharedAttributes(prods, ['country', 'material'], 1001)

result = [
  {
  country: 'china',
  material: 'steel',
  sku: 1453,
  groupId: 1001
},
{
  country: 'china',
  material: 'steel',
  sku: 4874,
  groupId: 1001
},
{
  country: 'japan',
  material: 'steel',
  sku: 4874,
  groupId: 1002
}
]

Answer №1

When using the groupProductsBySharedAttributes function, The initial step involves creating a Map object named groupIdMap. This map will store the stringified version of the object's groupIfIdentical keys from the product as a key and its corresponding groupId as a value. Additionally, you will create an array called groupedProducts, which will contain the products with their respective groupIds inserted. Next, iterate through the array of products, checking if the current key exists in the groupIdMap. If it does, the products are considered identical. If not, save the new key and corresponding groupId (after incrementing by one), and in both scenarios, add them to the groupedProducts array.

interface Product {
    [key: string]: any;
}

function productsAreIdentical(
    prod1: Product,
    prod2: Product,
    groupIfIdentical: (keyof Product)[]
): boolean {
    for (const key of groupIfIdentical) {
        if (prod1[key] !== prod2[key]) {
            return false;
        }
    }
    return true;
}

function groupProductsBySharedAttributes(
    products: Product[],
    groupIfIdentical: (keyof Product)[],
    initialGroupId: string,
): Product[] {
    let groupId = parseInt(initialGroupId); // Converting initialGroupId to an integer for easier incrementation
    const groupedProducts: Product[] = [];
    const groupIdMap = new Map<string, number>(); // Maps a stringified key to a groupId

    products.forEach(product => {
        let foundGroup = false;
        for (const [key, id] of groupIdMap) {
            const keyAttributes = JSON.parse(key);
            if (productsAreIdentical(product, keyAttributes, groupIfIdentical)) {
                product.groupId = id;
                foundGroup = true;
                break;
            }
        }

        if (!foundGroup) {
            const key = JSON.stringify(
                groupIfIdentical.reduce((acc, attr) => {
                    acc[attr] = product[attr];
                    return acc;
                }, {} as Partial<Product>)
            );

            if (!groupIdMap.has(key)) {
                groupIdMap.set(key, groupId);
                product.groupId = groupId;
                groupId++;
            }
        }

        groupedProducts.push(product);
    });

    return groupedProducts;
}

// Example usage
const prods = [
    { country: 'china', material: 'steel', sku: 1453 },
    { country: 'china', material: 'steel', sku: 4874 },
    { country: 'japan', material: 'steel', sku: 4874 }
];

const result = groupProductsBySharedAttributes(prods, ['country', 'material'], '1001');
console.log(result);

PLAYGROUND

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Identify potential interference with Sentry.captureMessage(...) or Sentry.captureException(...) functions within a JavaScript application, such as being obstructed by an ad-blocking extension

Overview Our Javascript application uses Angular and TypeScript, with Sentry.io for error reporting. If an error occurs, a custom modal allows users to leave a message, which should be sent to Sentry when they click submit. Issue We have noticed that mes ...

Trouble with sending arguments to Express middleware

I'm currently working on developing a custom input validation middleware using Express. My aim is to create a middleware that takes 2 parameters in order to validate client input effectively. Despite referencing various sources, including the Express ...

React and Redux are throwing an error stating that actions must be simple objects. For any asynchronous actions, it is recommended to utilize

Below is the code for my Component UserHandler import React, { Component } from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import * as actionCreators from '../../store/actions/ac ...

Developing various VueJS TypeScript projects utilizing a shared library

In the process of developing two VueJS applications using TypeScript, I have created one for public use and another as an admin tool exclusively for my own use. Both applications are being built and tested using vue-cli with a simple npm run serve command. ...

Retrieve an array from the updated scope

I need help with my code. How can I retrieve the names from an array and display them in my input box? Also, how do I get all the changed names back into the array? Thanks in advance! - Marco app.js var g[]; var names = ['John', 'Steve&apo ...

Tips for splitting JSON objects into individual arrays in React

I'm currently tackling a project that requires me to extract 2 JSON objects into separate arrays for use within the application. I want this process to be dynamic, as there may be varying numbers of objects inside the JSON array in the future - potent ...

Expanding jQuery Accordion on clicking a link within the panel

I have implemented an accordion feature on my webpage and I am looking to include hyperlinked text within each expanded panel. By clicking on the link 'Reduce text', I want to be able to collapse the accordion. How can I modify the existing code ...

In search of a JavaScript library that can help format strings to meet the requirements of JSON formatting

Utilizing jQuery ajax, I am transmitting updates from the client's browser to my server. However, I have noticed that there are certain characters not supported by JSON that require an additional "\" in front of each one to be properly sent. The ...

Hide <a> by setting its display property to none

Below is the HTML code: <td> <a class="link" href="#"> <span class="download">Link</span> </a> <a class="link" href="#"> <span class="csvdownload">Link 2</span> </a> </td> I am lo ...

Having trouble resolving a missing dependency warning with the useEffect React Hook in my Next.js app. Any tips on how to fix this

Currently, I'm facing the following warning: Warning: React Hook useEffect has a missing dependency: 'router'. Either include it or remove the dependency array Here is the code snippet from _app.js that seems to be causing this issue: cons ...

Just beginning my journey with Laravel alongside Vue.js and encountering the error message "Property or method is not defined on the instance but referenced during render."

Upon loading the page, all that can be seen is an empty screen following the brief appearance of {{greeting }}. Still getting acquainted with Vue, I decided to give it a go in laravel, so I put together a basic blade template: <!DOCTYPE html> <htm ...

Extracting certain elements from a text: a beginner's guide

I am currently developing a task manager that includes a feature to generate a PDF file using jsPDF. I am facing the challenge of extracting specific attributes from a string in order to print them as text utilizing jsPDF. The provided string is: [{" ...

Create a styled-components component that renders a cross inside a recognizable object surrounded by borders

Currently developing an Object Recognition app and aiming to add a border around the object along with a cross that indicates the center. The border has been successfully implemented, but having trouble creating the cross. The plan is to add two more boxes ...

Issue arising from using setCollideWorldBounds and overlap in Phaser 3

I'm facing an issue with Phaser 3. Whenever I use setCollideWorldBounds, I get an error saying "Cannot read property 'setCollideWorldBounds' of null" and the overlapping function doesn't seem to work. What's even more strange is t ...

"How can I open a DOCX file in a new window using JavaScript and then close the window

When attempting to open a doc/docx file in Word from an HTML link, my goal is to prevent the opening of a new browser window. First attempt: mywin=window.open("filename.docx","viewer"); The first attempt works fine, but it results in opening a new "view ...

The jQuery find and replace feature is causing my entire content to be replaced instead of just specific paragraphs

Within this section, there are multiple paragraphs and an input field. The concept is simple: the user inputs text into the box, then hits "ENTER" to trigger a jquery function below. The process involves identifying matches between the paragraph content a ...

Use the $.get() method in JavaScript to send a parameter to my controller function

My objective is to showcase the event details in a modal. To achieve this, I am running a JavaScript script that calls the "GetEventsDetails" method in my "Event" controller with the event ID. While debugging in Chrome, I can see the ID being passed corre ...

Looking to apply a filter using an object in lodash that includes a condition for a range of values

We have developed a video game database and now we need to filter users based on their game and rank preferences. The data structure is outlined below: [{ "key": 86, "position": [ 40.369, -3.5419 ], "info": { "gamesplayin ...

Mongoose makes sure that duplicate rows are not repeated in the database

I'm working with a basic mongoose schema definition below: const mongoose = require('mongoose'); const followSchema = new mongoose.Schema({ follower: { type: mongoose.Schema.Types.ObjectId, ref: 'User', ...

Transitioning images in JQuery by using a rollover effect

Is there a way to use Jquery for a hover effect that transitions images with a slide up animation? I've looked everywhere online, but I can't seem to find a code that works. All the examples I've found use fadeIn or fadeOut. Thank you for yo ...