How to extract and group an array by key

Viewed 540

I am building an Angular 9 app. In this app I have a dynamically fetched JSON array. I need to take the JSON array and then extract and group based upon the keys. Example array:

const collection = [{
      title: 'Product A',
      price: 234,
      cost: 234
    }, {
      title: 'Product B',
      price: 100,
      cost: 200
    }, {
      title: 'Product C',
      price: 344,
      cost: 55
    }, {
      title: 'Product D',
      price: 222,
      cost: 332
    }];

I can mange to extract individual keys but I want to have a method that takes any JSON array and then extract and group per key.

This is my code for extracting individual keys. I had to hard code the key name (title).

groupByKey(array, key) {
    return array.map(a => a.title);
  }

This is what I want to transform the original JSON array to:

[{
    header: "title",
    rows: ["Product A", "Product B", "Product C", "Product D"]
}, {
    header: "price",
    rows: [234, 100, 344, 222]
}, {
    header: "cost",
    rows: [234, 200, 55, 332]
}]
5 Answers

You can perform a reduce operation on the array, using an object to store all the values of each key.

const collection = [{
      title: 'Product A',
      price: 234,
      cost: 234
    }, {
      title: 'Product B',
      price: 100,
      cost: 200
    }, {
      title: 'Product C',
      price: 344,
      cost: 55
    }, {
      title: 'Product D',
      price: 222,
      cost: 332
    }];
function group(arr){
  return Object.values(
    arr.reduce((acc,curr)=>{
      Object.entries(curr).forEach(([k,v])=>{
        (acc[k] = acc[k] || {header: k, rows: []}).rows.push(v);
      });
      return acc;
    }, {})
  );
}
console.log(group(collection));

The simplest solution is:

const collection = [{
          title: 'Product A',
          price: 234,
          cost: 234
        }, {
          title: 'Product B',
          price: 100,
          cost: 200
        }, {
          title: 'Product C',
          price: 344,
          cost: 55
        }, {
          title: 'Product D',
          price: 222,
          cost: 332
        }];
    
    const transform = inputArray => {
      const headers = inputArray && inputArray[0] && Object.keys(inputArray[0]);
      return headers.map(header => 
      ({header: header, rows: collection.map(item => item[header])}));
    }
    
    console.log(transform(collection));

Out:=>

[
 { 
    header: 'title',
    rows: [ 'Product A', 'Product B', 'Product C', 'Product D' ] 
 },
 { 
    header: 'price',
    rows: [ 234, 100, 344, 222 ] 
 },
 { 
    header: 'cost',
    rows: [ 234, 200, 55, 332 ]
 }
]

You could do it non functional

const collection = [{
  title: 'Product A',
  price: 234,
  cost: 234
}, {
  title: 'Product B',
  price: 100,
  cost: 200
}, {
  title: 'Product C',
  price: 344,
  cost: 55
}, {
  title: 'Product D',
  price: 222,
  cost: 332
}]


const keys = Object.keys(collection[0]);
const arr = []
for (let i = 0; i < keys.length; i++) {
  const key = keys[i];
  const obj = {
    header: null,
    rows: []
  };
  for (let item of collection) {
    obj['header'] = key;
    obj['rows'].push(item[key])
  }
  arr.push(obj)
}
console.log(arr)

Approach using a Map, a simple loop and spread of Map#values()

const map = new Map(Object.keys(collection[0]).map(k => [k, {header:k, rows:[]}]));

collection.forEach(el =>Object.entries(el).forEach(([k, v]) => map.get(k).rows.push(v)))    

console.log( [...map.values()])
<script>
const collection=[{title:"Product A",price:234,cost:234},{title:"Product B",price:100,cost:200},{title:"Product C",price:344,cost:55},{title:"Product D",price:222,cost:332}];
</script>

This function will simply take in the entire collection and does not need a key. It assumes that each object in the collection has the same keys. 

const groupByKey = (collection) => {

  const result = []

  // return empty array if collection is empty
  if (collections.length === 0) return result

  // assumes that all json in the collection will have the same properties
  const keys = Object.keys(collection[0])


  keys.forEach(key => {
    const row = []

  collection.forEach(json => {
    row.push(json[key])
  })

  const formattedJSON = {
    header: key,
    row: row
  }

  result.push(formattedJSON)
  })

  return result
}

As a note, in your groupByKey function, to get the key dynamically, you can do:

a[title]

because a.title will literally look for a key called "title".

Related