0

Based on an array I wanna create a new array which is grouped by an property 'desc' of the contained objects. Like this:

const sourceArray = [
  { id: 'id1', sourceDesc: 'foo', prop1: 'ignoreme', prop2: 'ignoreme' }
  { id: 'id2', sourceDesc: 'foo', prop1: 'ignoreme', prop2: 'ignoreme' }
  { id: 'id3', sourceDesc: 'bar', prop1: 'ignoreme', prop2: 'ignoreme' }
  { id: 'id4', sourceDesc: 'baz', prop1: 'ignoreme', prop2: 'ignoreme' }
];
const targetArray = [
  { desc: 'foo', ids: [
    { id: 'id1', prop1: 'ignoreme', prop2: 'ignoreme' },
    { id: 'id2', prop1: 'ignoreme', prop2: 'ignoreme' }
  ]},
  { desc: 'bar', ids: [
    { id: 'id3', prop1: 'ignoreme', prop2: 'ignoreme' }
  ]},
  { desc: 'baz', ids: [
    { id: 'id4', prop1: 'ignoreme', prop2: 'ignoreme' }
  ]}
];

I guess the reduce() high-order-function will be the best / modern / efficient way to achieve this ... And if so, how? I'm a bit stuck in my head... I found some answers on this topic, but I'm not able to adapt them to my array structure :-(

6 Answers 6

1

By using .reduce() and .find() combination:

const sourceArray = [
  { id: 'id1', sourceDesc: 'foo', prop1: 'ignoreme', prop2: 'ignoreme' },
  { id: 'id2', sourceDesc: 'foo', prop1: 'ignoreme', prop2: 'ignoreme' },
  { id: 'id3', sourceDesc: 'bar', prop1: 'ignoreme', prop2: 'ignoreme' },
  { id: 'id4', sourceDesc: 'baz', prop1: 'ignoreme', prop2: 'ignoreme' }
];

const result = sourceArray.reduce((a, c) => {
  const found = a.find(e => e.desc === c.sourceDesc);
  
  if (found) found.ids.push({
    id: c.id,
    prop1: c.prop1,
    prop2: c.prop2
  });
  else a.push({
    desc: c.sourceDesc,
    ids: [{
      id: c.id,
      prop1: c.prop1,
      prop2: c.prop2
    }]
  });
  return a;
}, []);

console.log(result);

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

1 Comment

I learned a lot from all answers here, thanks! Although, with all the great ES6 features like Set, the spread operator and such like from Arun, I personally liked this solution the most in terms of readability.
1

You can get the desired output by using .reduce() along-with Object.entries() and .map() methods:

const data = [
  { id: 'id1', sourceDesc: 'foo', prop1: 'ignoreme', prop2: 'ignoreme' },
  { id: 'id2', sourceDesc: 'foo', prop1: 'ignoreme', prop2: 'ignoreme' },
  { id: 'id3', sourceDesc: 'bar', prop1: 'ignoreme', prop2: 'ignoreme' },
  { id: 'id4', sourceDesc: 'baz', prop1: 'ignoreme', prop2: 'ignoreme' }
];

const reducer = (arr) => Object.entries(
  arr.reduce((r, { sourceDesc:desc, ...rest }) => {
    r[desc] = r[desc] || [];
    r[desc].push(rest);
    return r;
  }, {})
).map(([k, v]) => ({desc: k, ids: v}));

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

Comments

1

You can use reduce like this

const sourceArray = [
  { id: 'id1', 'sourceDesc': 'foo', 'prop1': 'ignoreme', 'prop2': 'ignoreme' },
  { id: 'id2', 'sourceDesc': 'foo', 'prop1': 'ignoreme', 'prop2': 'ignoreme' },
  { id: 'id3', 'sourceDesc': 'bar', 'prop1': 'ignoreme', 'prop2': 'ignoreme' },
  { 'id': 'id4', 'sourceDesc': 'baz', 'prop1': 'ignoreme', 'prop2': 'ignoreme' }
];

const final = sourceArray.reduce((op,{id,sourceDesc,prop1,prop2})=>{
  let key= sourceDesc
  op[key] = op[key] || {des:sourceDesc, ids:[]}
  op[key].ids.push({id,prop1,prop2})
  return op
},{})

console.log(Object.values(final))

Comments

1

You can reduce the array to a Map, using sourceDesc as the key, and then spread the Map.values() iterator back to an array:

const sourceArray = [
  { id: 'id1', sourceDesc: 'foo', prop1: 'ignoreme', prop2: 'ignoreme' },
  { id: 'id2', sourceDesc: 'foo', prop1: 'ignoreme', prop2: 'ignoreme' },
  { id: 'id3', sourceDesc: 'bar', prop1: 'ignoreme', prop2: 'ignoreme' },
  { id: 'id4', sourceDesc: 'baz', prop1: 'ignoreme', prop2: 'ignoreme' }
];

const result = [...
  sourceArray.reduce((r, { sourceDesc: desc, ...o }) => {
    if(!r.has(desc)) r.set(desc, { desc, ids: [] }); // add the group if doesn't exist

    r.get(desc).ids.push(o); // add the object to the group

    return r;
  }, new Map)
.values()]; // spread the Map's values to get an array

console.log(result);

Comments

1

Here is another way using map, JSON array to Group by key:

const sourceArray = [{"id":"id1","sourceDesc":"foo","prop1":"ignoreme","prop2":"ignoreme"},{"id":"id2","sourceDesc":"foo","prop1":"ignoreme","prop2":"ignoreme"},{"id":"id3","sourceDesc":"bar","prop1":"ignoreme","prop2":"ignoreme"},{"id":"id4","sourceDesc":"baz","prop1":"ignoreme","prop2":"ignoreme"}];

const key = 'sourceDesc';

const arrayUniqueByKey1 = [...new Map(sourceArray.map(item =>
  [item[key], sourceArray.filter(x=>x[key] == item[key])]))
 /*remove following comment to get flat array*/ //.values()
];

console.log(arrayUniqueByKey1);

   /*OUTPUT Format
       [
  [
    "foo",
    [
      {
        "id": "id1",
        "sourceDesc": "foo",
        "prop1": "ignoreme",
        "prop2": "ignoreme"
      },
      {
        "id": "id2",
        "sourceDesc": "foo",
        "prop1": "ignoreme",
        "prop2": "ignoreme"
      }
    ]
  ]
]
   */

Comments

0

We can do with forEach and building object with keys as sourceDesc

const sourceArray = [
  { id: "id1", sourceDesc: "foo", prop1: "ignoreme", prop2: "ignoreme" },
  { id: "id2", sourceDesc: "foo", prop1: "ignoreme", prop2: "ignoreme" },
  { id: "id3", sourceDesc: "bar", prop1: "ignoreme", prop2: "ignoreme" },
  { id: "id4", sourceDesc: "baz", prop1: "ignoreme", prop2: "ignoreme" }
];

const update = data => {
  const res = {};
  data.forEach(({ sourceDesc, ...item }) => {
    if (!res[sourceDesc]) {
      res[sourceDesc] = { desc: sourceDesc, ids: [] };
    }
    res[sourceDesc].ids.push(item);
  });
  return Object.values(res);
};

console.log(update(sourceArray));

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.