1

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();

8 Answers 8

1

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);

Sign up to request clarification or add additional context in comments.

Comments

1

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);

Comments

1

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);

Comments

1

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)

2 Comments

Your answer was the most beautiful and simplest to read. Also it helps to solve my problem and you did not use other for-loops or conditions which is great. But it will not be compatible in IE. Thanks
@DorinMusteața I think all answers posted here use some ES6 features which doesn't work on IE :)
0

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>

Comments

0

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>

Comments

0

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))

Comments

0
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));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.