Pair two arrays based on a specific key value

I'm currently facing a challenge where I need to associate two arrays of objects, and I could really use some help with this. Explanation :

One array contains various reference products

const array1 = [
{name: peanuts, referenceKey: 0}, 
{name: almond, referenceKey: 1}, 
{name: nuts, referenceKey: 2}, 
{name: cream, referenceKey: 3}
] 

The second array consists of open reference products with expiration dates and matching reference keys from array1, along with specific keys for each open product

const array2 = [
 {name: peanuts, expirationDate: "30d", referenceKey:0, otherKey: 42}, 
 {name: peanuts, expirationDate: "20d", referenceKey:0, otherKey: 43}, 
 {name: peanuts, expirationDate: "15h", referenceKey:0, otherKey: 44}, 
 {name: almond, expirationDate: "30d", referenceKey:1, otherKey: 45},
 {name: cream, expirationDate: "1d", referenceKey: 3, otherKey: 46},
 {name:cream, expirationDate: "12h", referenceKey: 3, otherKey: 47}
] 

My goal is to calculate the number of the same products in array2 that are open, and store this count in a new array based on array1, like so:

const array3 = [
 {name: peanuts, referenceKey: 0, opened: 3}, 
 {name: almond, referenceKey: 1, opened: 1}, 
 {name: nuts, referenceKey: 2, opened: 0}, 
 {name: cream, referenceKey: 3, opened: 2}
] 

I attempted to group array2 by name using the Reduce() method as shown below :

    const groupByName = (products, name) => {
        return products.reduce((acc, obj) => {
            var key = obj[name];
            if (!acc[key]) {
                acc[key] = []
            }
            acc[key].push(obj);
            return acc
        }, [])
    };

    const groupByName = groupByReference(array2, "name")
    console.log(groupByName)
    

output of groupByName:

  [
    [peanuts:
    [
        {name: peanuts, expirationDate: "30d", referenceKey: 0, otherKey: 42}, 
        {name: peanuts, expirationDate: "20d", referenceKey:0, otherKey: 43}, 
        {name: peanuts, expirationDate: "15h", referenceKey:0, otherKey: 44}, 
    ],
    cream: [
        {name: cream, expirationDate: "1d", referenceKey: 3, otherKey: 46 },
        {name: cream, expirationDate: "12h", referenceKey: 3, otherKey: 47}
    ],
    almond: [
        {name: almond, expirationDate: "30d", referenceKey:1, otherKey: 45},
    ]
 ]

Next, I tried to determine the length of each array but faced difficulties. Despite attempting to utilize the Map() method, it did not provide me with the expected results.

Even when explicitly mentioning an index like groupByName['peanuts'], the console.log() returned the correct array. However, trying to access groupByName['peanuts'].length did not work as intended.

Answer №1

To achieve the desired output and keep track of duplicate objects with the same name, you can utilize an object in your code logic.

const
    items1 = [{ name: "peanuts", identifier: 0 }, { name: "almond", identifier: 1 }, { name: "nuts", identifier: 2 }, { name: "cream", identifier: 3 }],
    items2 = [{ name: "peanuts", expiry: "30d", identifier: 0, key: 42 }, { name: "peanuts", expiry: "20d", identifier: 0, key: 43 }, { name: "peanuts", expiry: "15h", identifier: 0, key: 44 }, { name: "almond", expiry: "30d", identifier: 1, key: 45 }, { name: "cream", expiry: "1d", identifier: 3, key: 46 }, { name: "cream", expiry: "12h", identifier: 3, key: 47 }],
    tempObject = {},
    resultSet = items1.map(obj => tempObject[obj.name] = { ...obj, count: 0 });

items2.forEach(obj => tempObject[obj.name].count++);

console.log(resultSet);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Answer №2

const newArr = [...originalArr];
for (let item of newArr) {
  item['status'] = referenceArr.filter(el => el.key === item.key).length;
}

Answer №3

Based on your initial request, I believe this is the solution you are looking for if I have interpreted your question correctly.

You have the option to group by either name or referenceKey since they are present in both arrays, but not by expirationDate.

const array1 = [
  { name: "apples", referenceKey: 0 },
  { name: "bananas", referenceKey: 1 },
  { name: "oranges", referenceKey: 2 },
  { name: "pears", referenceKey: 3 },
];

const array2 = [
  { name: "apples", expirationDate: "5d", referenceKey: 0, quantity: 4 },
  { name: "apples", expirationDate: "3d", referenceKey: 0, quantity: 5 },
  { name: "apples", expirationDate: "8h", referenceKey: 0, quantity: 6 },
  { name: "bananas", expirationDate: "7d", referenceKey: 1, quantity: 7 },
  { name: "pears", expirationDate: "1d", referenceKey: 3, quantity: 8 },
  { name: "pears", expirationDate: "20h", referenceKey: 3, quantity: 9 },
];

const groupByKey = (fruits, instances, key) => {
  return fruits.reduce((acc, obj) => {
    // Count instances that match
    const matching = instances.filter((instance) => instance[key] === obj[key])
      .length;

    // Add to accumulator array using spread operator for immutability.
    acc = [
      ...acc,
      {
        name: obj.name,
        referenceKey: obj.referenceKey,
        matching,
      },
    ];

    return acc;
  }, []);
};

const array3 = groupByKey(array1, array2, "name");
console.log(array3);

Answer №4

To kick things off, I suggest implementing a helper function that tallies all objects containing a specific value when a provided function is applied to them. Here's an example:

countItemsByAttribute(x => x.name)([{name: 'apple', type: 'fruit'}, {name: 'orange', type: 'fruit'}, 
                     {name: 'apple', type: 'fruit'}, {name: 'banana', type: 'fruit'}])
//=> {apple: 2, orange: 1, banana: 1}

This helps us count our objects based on their category (if type is intended to be a string and not reference). Then we can easily loop through our object definitions, incorporating the relevant quantity property from the result (or defaulting to 0 if none found.)

The implementation could resemble this:

const countItemsByAttribute = (fn) => (items) => 
  items.reduce((acc, item, _, __, key = fn(item)) => ({...acc, [key]: (acc[key] || 0) + 1}), {})

const quantity = (definitions, instances, counts = countItemsByAttribute (x => x.category) (instances)) =>
  definitions.map(def => ({...def, quantity: counts[def.category] || 0}))

const productArray = [{product: 'apple', category: 0}, {product: 'orange', category: 1}, {product: 'banana', category: 2}, {product: 'grapefruit', category: 3}]
const inventoryArray = [{product: 'apple', quantityInStock: 10, category: 0}, {product: 'apple', quantityInStock: 5, category: 0}, {product: 'banana', quantityInStock: 20, category: 1}] 

console.log(quantity(productArray, inventoryArray))
.as-console-wrapper {max-height: 100% !important; top: 0}


Your idea was definitely heading in the right direction. You could make use of your groupProducts (after resolving a naming conflict between two versions of groupProducts and one of groupByCategory) with a method similar to this:

const groupedByProduct = groupProducts(inventoryArray, "product")

productArray.map(({product, ...rest}) => ({
  product, 
  ...rest, 
  quantity: (groupedByProduct[product] || []).length
}))

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

The React Testing Library encountered an error: TypeError - actImplementation function not found

Encountering a TypeError: actImplementation is not a function error while testing out this component import React from 'react'; import { StyledHeaderContainer, StyledTitle } from './styled'; export const Header = () => { return ( ...

Getting the x-axis points on Google charts to start at the far left point

I have integrated the Google API for charts into my application and am using multiple charts on the same page. However, I am facing an issue with setting padding for the charts. When I include more data points, the chart area occupies more space in the div ...

Turn off hover effect for the v-checkbox component in Vuetify 2

Is there a way to prevent the darkened circle from appearing behind a v-checkbox in Vuetify 2 when hovering over it? My v-checkbox is currently enclosed within a v-tab and a v-tooltip, although I'm not sure if that has any impact. <v-tab v-for=&quo ...

What is the best way to implement a new search input for my datatable while also enabling the searchHighlight feature?

I'm attempting to implement a search bar for my datatables. I need to hide the default search engine that comes with datatables, so I added a script that I found in a forum. However, when I try to run it in my code, it doesn't work and displays a ...

Navigating through Sails.Js: A Guide to Implementing Pagination

Looking to implement paginated tables in your application using sails.js, mongodb, and waterline-ORM? Wondering if there is a recommended method for pagination in sails.js? ...

Sharing data between view and controller in Laravel: a guide

Currently, I am developing a product cart feature that includes subtotal and grand total values displayed within <span> tags. My goal is to pass the grand total value to the controller for further processing. However, I encountered an issue where the ...

Currently in motion post file selection

I am currently facing an issue with a button that triggers a file selector pop-up. Below is the code snippet: <button mat-raised-button (click)="inputFile.click()">Choose a file</button> <input #inputFile type="file" [style.display]="' ...

Jpicker is renowned for its transparency feature

I am currently using the Jpicker jpicker-1.1.6.js script which can be found at Below is a snippet of my code: <script type="text/javascript"> $(function() { $.fn.jPicker.defaults.images.clientPath='/img'; var ...

Formulate a targeted search request according to the selected radio button option

I'm working on a form that looks like this: <form> <input type="text" id="searchedWord" class="form-control form-control-lg" value="words here"/> <button type="submit" id="f ...

Removing the element generated by Angular from the DOM

I've hit a roadblock with this issue. Here is the delete function in my mainController. $scope.delete = function($posts) { $http.delete('/api/posts/' + $posts._id) .success(function(data) { // remove element from DOM ...

Invoking a parent controller method from a directive in AngularJS

I have utilized a tree grid plugin from the following link: https://github.com/khan4019/tree-grid-directive and I made some customizations to its template: .directive('treeGrid', [ '$timeout', function($timeout) { return { ...

Switch up your code and toggle a class on or off for all elements that share a specific class

I've been attempting to create a functionality where, upon clicking a switch, a specific class gets added to every element that is assigned the class "ChangeColors". Unfortunately, I have encountered some difficulties in achieving this task. The error ...

How to incorporate a custom JavaScript file into an Angular 7 application

Suppose there is a JavaScript file named mylib.js in an angular 7 application, located at assets/mylib.js: mylib = function(){ return { hi: function() { alert('hi'); } }; }(); If I want to be able to call mylib.hi() in my hero-f ...

A unique technique for creating a stunning visual effect with images using

Can anyone help me with this issue: Check out this animated GIF The images in my project are overlapping when scrolling! How can I achieve a similar effect for my images? Is there a tutorial or plugin available for this? Here is the current code sn ...

Why are imported modules unable to reach global variables in Node?

As I delve deeper into using ES6 and organizing my code across multiple files for better readability and version control, I've encountered an issue that has me puzzled. In one of my scenarios, I have a class defined in a separate file called class.js, ...

What is the process to assign a value received from the server to an Input field and then update

I am working on an input field that should initially display a value from the server const [nameValue, setNameValue] = useState(""); <TextField id="outlined-read-only-input" label="Display Nam ...

Tips for combining Nuxt with Vue Cli 3

section: My current setup involves utilizing Nuxt.js to set up my Vue application. I'm curious if it's feasible to incorporate Nuxt into a Vue Cli 3 project in order to leverage the advantages of both platforms. Surprisingly, there doesn't ...

When clearInterval is used to stop a setInterval, it will not automatically restart if reset with setInterval

I am facing an issue with a countdown timer that I have created using setInterval in JavaScript. The timer is supposed to countdown from one minute at one second intervals. However, when I click the "start" button, it starts the countdown but if I click an ...

Customized Grafana dashboard with scripted elements

I'm running into an issue while using grafana with graphite data. When I attempt to parse the data, I encounter an error due to the server not providing a JSON response. I am experimenting with scripted dashboards and utilizing the script found here: ...

Accordion checkbox with dynamic features

Currently, I am dynamically populating data into a jQuery accordion. My goal is to include a checkbox just before the <h2> text. <div id="checkbox"> <h2> <span> <input type="checkbox" class="mycheck" value="apple" / ...