Combine objects within an array based on a specific property value in JavaScript

Is there an easy way to aggregate an array of objects based on a specific property in JavaScript?

[ { key: 'black', value: [ '2', '3', '9' ] },
  { key: 'black', value: [ '1' ] },
  { key: 'gold', value: [ '2', '3' ] },
  { key: 'gold', value: [ '1' ] },
  { key: 'red', value: [ '9' ] },
  { key: 'white', value: [ '2', '3' ] },
  { key: 'white', value: [ '1' ] } ]

I want to transform the above array into:

[ { key: 'black', value: [ '1', '2', '3', '9' ] },
  { key: 'gold', value: [ '1', '2', '3' ] },
  { key: 'red', value: [ '9' ] },
  { key: 'white', value: [ '1', '2', '3' ] } ]

If anyone knows a simple solution using lodash or Array.reduce, I would greatly appreciate the help!

Answer №1

To streamline the process, consider using a temporary object to reference the groups and generate an array containing the outcome within a single iteration.

const data = [{ key: 'black', value: ['2', '3', '9'] }, { key: 'black', value: ['1'] }, { key: 'gold', value: ['2', '3'] }, { key: 'gold', value: ['1'] }, { key: 'red', value: ['9'] }, { key: 'white', value: ['2', '3'] }, { key: 'white', value: ['1'] }],
    resultArray = [];

data.forEach(function (item) {
    if (!this[item.key]) {
        this[item.key] = { key: item.key, value: [] };
        resultArray.push(this[item.key]);
    }
    this[item.key].value = this[item.key].value.concat(item.value);
    this[item.key].value.sort();
}, {});

document.write('<pre>' + JSON.stringify(resultArray, 0, 4) + '</pre>');

Answer №2

If you want to tackle this task on your own without relying on external libraries, here's a straightforward approach:

let input = ...
let reduced = [];
let reducedMap = {};

input.forEach((obj) => {
  if (!reduced.hasOwnProperty(obj.key)) {
    reduced[obj.key] = [];
  }

  for (let i = 0; i < obj.value.length; i++) {
    if (reduced[obj.key].indexOf(obj.value[i]) < 0) {
      reduced[obj.key].push(obj.value[i]);
    }
  }
});

reduced = Object.keys(reducedMap).map((key) => {
  return {
    key: key,
    value: reducedMap[key].sort()
  };
});

Answer №3

To achieve the desired output, you should first group the data in a map and then manipulate it accordingly.

var output = {};
obj.forEach(function(item){
 output[item.key] = output[item.key] || [];
 output[item.key] = output[item.key].concat(item.value);
});

Next, generate the compressed output as follows:

var finalOutput = Object.keys(output).map(function(item){
      return { key: item, value: output[item] };
});

Check out the demo below to see this in action:

var obj = [{
  key: 'black',
  value: ['2', '3', '9']
}, {
  key: 'black',
  value: ['1']
}, {
  key: 'gold',
  value: ['2', '3']
}, {
  key: 'gold',
  value: ['1']
}, {
  key: 'red',
  value: ['9']
}, {
  key: 'white',
  value: ['2', '3']
}, {
  key: 'white',
  value: ['1']
}];

var output = {};
obj.forEach(function(item) {
  var key = item.key;
  var value = item.value;
  output[key] = output[key] || [];
  output[key] = output[key].concat(item.value);
});

var finalOutput = Object.keys(output).map(function(item) {
  return {
    key: item,
    value: output[item]
  };
});

document.body.innerHTML += JSON.stringify(finalOutput,0,4);

Answer №4

To achieve the desired outcome, you can utilize a mixture of groupBy, map, and reduce:

var solution = 
    _(data)
        .groupBy('category')
        .map(function(items, category) {
            return {
                category: category, 
                values: _.reduce(items, function(acc, item) {
                    return acc.concat(item.value);
                }, []).sort(),
            };
        })
        .value();

Check out this working example on: https://jsfiddle.net/abc123xyz/1/

Modern ES2015 Syntax Version

var solution = 
    _(data)
        .groupBy('category')
        .map((items, category) => ({
            category, 
            values: _.reduce(items, (acc, item) => acc.concat(item.value), []).sort()
        }))
        .value();

Explore the implementation here: https://jsfiddle.net/abc123xyz/2/

Answer №5

const items = [{ key: 'black', value: ['2', '3', '9'] }, { key: 'black', value: ['1'] }, { key: 'gold', value: ['2', '3'] }, { key: 'gold', value: ['1'] }, { key: 'red', value: ['9'] }, { key: 'white', value: ['2', '3'] }, { key: 'white', value: ['1'] }],
    resultArray = [];

items.forEach(function(item) {
    if (!this[item.key]) {
        this[item.key] = { key: item.key, value: [] };
        resultArray.push(this[item.key]);
    }
    this[item.key].value = this[item.key].value.concat(item.value);
    this[item.key].value.sort();
}, {});

document.write('<pre>' + JSON.stringify(resultArray, 0, 4) + '</pre>');

Answer №6

When it comes to working with lodash, the tags in your question have you covered:

const arr = [ { key: 'black', value: [ '2', '3', '9' ] },
      { key: 'black', value: [ '1' ] },
      { key: 'gold', value: [ '2', '3' ] },
      { key: 'gold', value: [ '1' ] },
      { key: 'red', value: [ '9' ] },
      { key: 'white', value: [ '2', '3' ] },
      { key: 'white', value: [ '1' ] } ];
      
    const newArr = _.chain(arr) // Lodash<number, { key, value }>
        .groupBy("key") // Lodash<key, { key, value }>
        .mapValues(items => {
            return _.chain(items) // Lodash<number, value>
                .reduce((values, item) => [...values, ...item.value], []) // Lodash<number, value>
                .sortBy(_.identity)
                .sortedUniq()
                .value(); // value
        }) // Lodash<key, value>
        .map((value, key) => ({ key, value }))  // Lodash<number, { key, value }>
        .value(); // { key, value }[]
    
    console.log({ arr, newArr });
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

The utilization of _.chain facilitates a series of transformations, while _.value is employed to reveal the end result of those transformations.

Answer №7

function removeDuplicates(arr) {
   return [...new Set(arr)].sort();
}

const data = [
  { category: 'black', items: ['2', '3', '9', '1'] },
  { category: 'black', items: ['1'] },
  { category: 'gold', items: ['2', '3'] },
  { category: 'gold', items: ['1'] },
  { category: 'red', items: ['9'] },
  { category: 'white', items: ['3', '2'] },
  { category: 'white', items: ['1'] }
]

removeDuplicates(data.map(item => item.category)) // get unique, sorted array of categories
  .map(category =>
    data
      .filter(item => item.category === category) // process each category in turn
      .reduce((accumulator, current) => ({
        category,
        items: removeDuplicates([...current.items, ...accumulator.items]) // aggregate and sort the items for each category
      }))
  )

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

How to Properly Manipulate DOM Elements in an Angular Directive

My directive, powerEntry, has different CSS classes that I want to add or remove based on the model state. Currently, in my link function, I have some logic like this: Script.JS if (calcState.availablePoints > 0 && isHighEnoughLevel) { ...

In PHP, you can use the `echo` statement to output an HTML input

Incorporating HTML into PHP using heredoc methodology can sometimes lead to challenges when trying to retrieve user input variables. Attempting to access the input variable with $_GET["input"] may result in an error message indicating an undefined index: ...

What is the best approach to combine 'v-for' with 'v-if' in my code?

I have a challenge with two tables - one for books and the other for stock. I am attempting to retrieve books by their name and display them in the stock table. The code snippet I've used below is resulting in an error message being displayed. [v ...

How can you efficiently pass the index as a prop to a child component in React.js when dealing with arrays stored in

Just starting out with React, so bear with me if my terminology is a bit off. I'm working on a table that displays a list of people in a specific order. I want to be able to assign a this.props.tablePosition value based on the index of each person. t ...

Can you explain Node.js and its applications as well as how it is commonly used?

A while back, during my time at IBM when I was immersed in learning, I came across something known as BlueMix, a cloud product. Within BlueMix, there was a rather primitive component called Node.js. Since that moment, I've been filled with curiosity a ...

What is the best way to form an array containing associative subarrays from a one-dimensional array?

Below is an array that I am working with. Array( [1041] => 30 [1046] => 10 [1047] => 10 ) I would like to restructure it as follows. Array([0] => Array ( [material_name] => 1041 [material_qty] => 30 ) [1] => Array ( [ma ...

Node.js server for Cross-Origin Resource Sharing

I'm brand new to Node.js and I'm trying to run some example code, but I keep encountering CORS issues. Server.js var http = require('http'); var io = require('socket.io'); server = http.createServer(function(req, r ...

Maintaining consistent spacing across multiple lines with a flex-box container: A guide

Here is my starting point: Click here The spacing I have on the sides of the container is larger than between the content. When resizing the window, the elements eventually wrap to the second line. I want to set a breakpoint so that when the content move ...

Eliminate the first element from the array without erasing the entire array

I'm struggling to create a script that will effectively remove any empty elements from my array. The issue I'm running into is that there's an empty element in the [0] slot. So, when I attempt to unset the value, it ends up deleting the ent ...

componentWillReceiveProps with excessive conditional statements

Having recently ventured into React development, I've already worked on 3 major projects utilizing React+Redux. However, I've noticed a recurring pattern that I find quite displeasing: componentWillReceiveProps(nextProps) { if (nextProps.par ...

Error: Unexpected token '<' encountered in Ajax request with status code 200

While attempting to run an ajax request, I keep encountering the error message 'Syntax Error: unexpected token <' along with a xhr.status of 200. Strangely, I am able to successfully email the variable passed from the JavaScript file to the PH ...

Show loading icon while resolving routes or changing routes

I am attempting to display a loading icon while the route resolver is fetching data from the database. Here is what I have tried: Main Component: _router.events.subscribe((routerEvent: RouterEvent) => { if (routerEvent instanceof NavigationStart) ...

Accepting POST requests from an external source in AngularJS

I am currently working on an application that utilizes AngularJS 1.4.0 and requires the ability to receive POST data from an external source. In AngularJS, routes often use parameters in the URL format like this: .when('/section/:param', { t ...

Sharing functions as properties with child components

If I have a reusable component called Modal in my application and I want to dynamically bind functions to the Yes button, how can I pass a function to the @click event of the Yes button within the Modal as a prop? For example: //data tags are used for fas ...

Angular code is malfunctioning and not delivering the expected results

I currently have setup the code below: var videoControllers = angular.module('videoControllers', []); videoControllers.videoControllers('VideoDetailController', function($scope, $routeParams, $http){ $http.get('http://localho ...

Discovering elements with Selenium

While using selenium, I stumbled upon this web element: <td style="padding-right: 10px; " **onclick="javascript:show_me('CarDetails.php?CarID=2358912&SubCatID=1**', '2358912', 560, 'ActiveLinkVisited');stat( '../& ...

Changing the names of elements in a struct array using Matlab

In my struct array, Links, the data is structured as shown below: ================== src dest type ================== dev_1 sw_1 S d_2 sw_3 S ev_4 sw_2 S DND sw_1 Y sw_3 DND Y sw_1 dev_1 S sw_2 ev_4 S sw_2 sw_1 D ...

Encountered an Error in Express.js: Unable to POST /users

I am currently in the process of learning the MEAN stack by following a tutorial and have encountered an error. Unfortunately, I am having difficulty identifying exactly where I went wrong. While attempting to test routes in Postman by creating a user, I ...

Activate Keyboard and Background in the Bootstrap Modal

I have set up my modal to disable the escape key and backdrop by default. $(modal).modal({ backdrop: "static", keyboard: false }); However, at a later time, I want to enable them again. $(modal).modal({ backdrop: true, keyboard: true }); The is ...

Showing a series of text entries one after the other, each appearing with a smooth fade-in and fade-out

I am eager to showcase a list of text in a sequential order, implementing a fade-in/fade-out effect and concluding with the display of an image. Additionally, I aim to have all the text centered on the page. <div>a<div> <div>b</div> ...