Dorin Musteața
Dorin Musteața

Reputation: 160

Array of objects, group by object key:value

I have an array of objects like this:

const data = [
      { id: 1, type: { name: "A" }, condition: "new" },
      { id: 2, type: { name: "C" }, condition: "broken" },
      { id: 3, type: { name: "C" }, condition: "brand new" },
      { id: 4, type: { name: "B" }, condition: "broken" },
      { id: 5, type: { name: "C" }, condition: "brand new" },
      { id: 6, type: { name: "A" }, condition: "new" },
      { id: 7, type: { name: "A" }, condition: "new" },
      { id: 8, type: { name: "B" }, condition: "broken" },
      { id: 9, type: { name: "C" }, condition: "broken" }
    ];

What i'm trying to do is to group those objects by type's key:value in this case by name:value, like this:

const data = [
      {
        by: {
          type: { name: "A" }
        },
        chunks: [
          { id: 1, type: { name: "A" }, condition: "new" },
          { id: 6, type: { name: "A" }, condition: "new" },
          { id: 7, type: { name: "A" }, condition: "new" }
        ]
      },
      {
        by: {
          type: { name: "C" }
        },
        chunks: [
          { id: 2, type: { name: "C" }, condition: "broken" },
          { id: 3, type: { name: "C" }, condition: "brand new" },
          { id: 5, type: { name: "C" }, condition: "brand new" },
          { id: 9, type: { name: "C" }, condition: "broken" }
        ]
      },
      {
        by: {
          type: { name: "B" }
        },
        chunks: [
          { id: 4, type: { name: "B" }, condition: "broken" },
          { id: 8, type: { name: "B" }, condition: "broken" }
        ]
      },
    ];

I've tried to use lodash and Array.prototype.reduce() , my last attempt was using lodash, but i can't get the type as object.

_.chain(channels)
      .groupBy("type.name")
      .map((item, i) => {
        return {
          chunks: item,
          by: i
        };
      })
      .value();

Upvotes: 1

Views: 209

Answers (8)

Aurélien
Aurélien

Reputation: 91

const data = [
    { id: 1, type: { name: "A" }, condition: "new" },
    { id: 2, type: { name: "C" }, condition: "broken" },
    { id: 3, type: { name: "C" }, condition: "brand new" },
    { id: 4, type: { name: "B" }, condition: "broken" },
    { id: 5, type: { name: "C" }, condition: "brand new" },
    { id: 6, type: { name: "A" }, condition: "new" },
    { id: 7, type: { name: "A" }, condition: "new" },
    { id: 8, type: { name: "B" }, condition: "broken" },
    { id: 9, type: { name: "C" }, condition: "broken" }
];

const data2 = {};

data.forEach(test => {

    if (data2[test.type.name]) {
        data2[test.type.name].chunks.push({
            test
        });
    } else {
        data2[test.type.name] = {
            by: {
                type: test.type
            },
            chunks: [
                test
            ]
        };
    }
});

console.log(Object.values(data2));

Upvotes: 0

adiga
adiga

Reputation: 35242

You could use reduce and Object.values to group like this:

const data = [{"id":1,"type":{"name":"A"},"condition":"new"},{"id":2,"type":{"name":"C"},"condition":"broken"},{"id":3,"type":{"name":"C"},"condition":"brand new"},{"id":4,"type":{"name":"B"},"condition":"broken"},{"id":5,"type":{"name":"C"},"condition":"brand new"},{"id":6,"type":{"name":"A"},"condition":"new"},{"id":7,"type":{"name":"A"},"condition":"new"},{"id":8,"type":{"name":"B"},"condition":"broken"},{"id":9,"type":{"name":"C"},"condition":"broken"}];
    
const merged = data.reduce((acc, o) => {
  const {type} = o;
  acc[type.name] =  acc[type.name] || { by: { type }, chunks:[] };
  acc[type.name].chunks.push(o)
  return acc;
},{})
    
const output = Object.values(merged)
console.log(output)

(Ignore snippet console and check browser's console)

Upvotes: 1

Code Maniac
Code Maniac

Reputation: 37755

You can use reduce

Here the idea is

  • Use name as key and checks if op already has that particular key or not. if the key is already there we push the inp to chunks property of op. if not we add a new key with by and chunks with respective values.

const data = [{ id: 1, type: { name: "A" }, condition: "new" },{ id: 2, type: { name: "C" }, condition: "broken" },{ id: 3, type: { name: "C" }, condition: "brand new" },{ id: 4, type: { name: "B" }, condition: "broken" },{ id: 5, type: { name: "C" }, condition: "brand new" },{ id: 6, type: { name: "A" }, condition: "new" },{ id: 7, type: { name: "A" }, condition: "new" },{ id: 8, type: { name: "B" }, condition: "broken" },{ id: 9, type: { name: "C" }, condition: "broken" }];
 
let op = data.reduce( (op,inp) => {
  if( op[inp.type] ){
    op[inp].chunks.push(inp)
  } else {
    op[inp] = {
      by: {...inp.type},
      chunks: [inp]
    }
  }
  return op
},{})

console.log(Object.values(op))

Upvotes: 0

Ori Drori
Ori Drori

Reputation: 192016

Since you can't group by an object because two objects with the same keys and values are not the same object, you can use JSON.stringify() to convert the object to a string, and group by it. Use Array.reduce() to group the items by the stringified object. Convert to pairs using Object.entries(), then map the values to an object, and extract the by value using JSON.parse():

const groupByObj = (arr, key) => Object.entries(
  arr.reduce((r, o) => {
    const k = JSON.stringify({ [key]: o[key] });
    
    r[k] = r[k] || [];
    
    r[k].push(o);
    
    return r;
  }, {})
).map(([k, chunks]) => ({
  by: JSON.parse(k),
  chunks
}));

const data = [{"id":1,"type":{"name":"A"},"condition":"new"},{"id":2,"type":{"name":"C"},"condition":"broken"},{"id":3,"type":{"name":"C"},"condition":"brand new"},{"id":4,"type":{"name":"B"},"condition":"broken"},{"id":5,"type":{"name":"C"},"condition":"brand new"},{"id":6,"type":{"name":"A"},"condition":"new"},{"id":7,"type":{"name":"A"},"condition":"new"},{"id":8,"type":{"name":"B"},"condition":"broken"},{"id":9,"type":{"name":"C"},"condition":"broken"}];

const result = groupByObj(data, 'type');

console.log(result);

Upvotes: 1

Shubham Khatri
Shubham Khatri

Reputation: 281804

You just need to change the way you assing the value to by in map. Assign like

 return {
      chunks: item,
      by: { type: item[0].type} 
    };

const data = [
      { id: 1, type: { name: "A" }, condition: "new" },
      { id: 2, type: { name: "C" }, condition: "broken" },
      { id: 3, type: { name: "C" }, condition: "brand new" },
      { id: 4, type: { name: "B" }, condition: "broken" },
      { id: 5, type: { name: "C" }, condition: "brand new" },
      { id: 6, type: { name: "A" }, condition: "new" },
      { id: 7, type: { name: "A" }, condition: "new" },
      { id: 8, type: { name: "B" }, condition: "broken" },
      { id: 9, type: { name: "C" }, condition: "broken" }
    ];
    
    console.log(_.chain(data)
      .groupBy("type.name")
      .map((item, i) => {
        return {
          by: { type: item[0].type},
          chunks: item
        };
      })
      .value())
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Upvotes: 0

Krzysztof Krzeszewski
Krzysztof Krzeszewski

Reputation: 6749

Using lodash as you mentioned before we could do:

const data = [
  { id: 1, type: { name: "A" }, condition: "new" },
  { id: 2, type: { name: "C" }, condition: "broken" },
  { id: 3, type: { name: "C" }, condition: "brand new" },
  { id: 4, type: { name: "B" }, condition: "broken" },
  { id: 5, type: { name: "C" }, condition: "brand new" },
  { id: 6, type: { name: "A" }, condition: "new" },
  { id: 7, type: { name: "A" }, condition: "new" },
  { id: 8, type: { name: "B" }, condition: "broken" },
  { id: 9, type: { name: "C" }, condition: "broken" }
];
const groups = _.groupBy(data, (val) => val.type.name)
const formattedGroupd = Object.keys(groups).map(groupKey => {
  return {
    by: {type: { name: groupKey }},
    chunks: groups[groupKey]
  }
});
console.log(formattedGroupd)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Upvotes: 0

Nino Filiu
Nino Filiu

Reputation: 18513

Here you go:

const data = [
      { id: 1, type: { name: "A" }, condition: "new" },
      { id: 2, type: { name: "C" }, condition: "broken" },
      { id: 3, type: { name: "C" }, condition: "brand new" },
      { id: 4, type: { name: "B" }, condition: "broken" },
      { id: 5, type: { name: "C" }, condition: "brand new" },
      { id: 6, type: { name: "A" }, condition: "new" },
      { id: 7, type: { name: "A" }, condition: "new" },
      { id: 8, type: { name: "B" }, condition: "broken" },
      { id: 9, type: { name: "C" }, condition: "broken" }
];

let names = [];
data.forEach(x => {
  if (names.indexOf(x.type.name)==-1) {
    names.push(x.type.name);
  }
});

let grouped = names.map(name => ({
  by: { type: {name}},
  chunks: data.filter(x => x.type.name==name)
}));
console.log(grouped);

Upvotes: 1

trincot
trincot

Reputation: 350365

You can use a Map to collect the data by group, and then its values() method will iterate the data as you want:

const data = [{ id: 1, type: { name: "A" }, condition: "new" },{ id: 2, type: { name: "C" }, condition: "broken" },{ id: 3, type: { name: "C" }, condition: "brand new" },{ id: 4, type: { name: "B" }, condition: "broken" },{ id: 5, type: { name: "C" }, condition: "brand new" },{ id: 6, type: { name: "A" }, condition: "new" },{ id: 7, type: { name: "A" }, condition: "new" },{ id: 8, type: { name: "B" }, condition: "broken" },{ id: 9, type: { name: "C" }, condition: "broken" }];

const map = new Map(data.map(o => [o.type.name, { by: { type: o.type.name }, chunks: [] }]));
data.forEach(o => map.get(o.type.name).chunks.push(o));
const result = [...map.values()];

console.log(result);

Upvotes: 1

Related Questions