0

I have the below user information.

[ { ID: '21',NAME: 'JACK',MARKS: 75,GRADE: 'A',RESULT: 'PASS ' },
{ ID: '21',NAME: 'JACK',MARKS: 32,GRADE: 'F',RESULT: 'FAIL' },
{ ID: '87',NAME: 'SAM',MARKS: 91,GRADE: 'A+',RESULT: 'PASS' },
{ID: '7',NAME: 'TOM',MARKS: 100,GRADE: 'A+',RESULT: 'PASS' },
{ ID: '21',NAME: 'JACK',MARKS: 61,GRADE: 'B',RESULT: 'PASS' },
{ ID: '87',NAME: 'SAM',MARKS: 72,GRADE: 'B',RESULT: 'PASS ' }
]

Goal is to generate a new object called INFO with fields MARKS,GRADE,RESULT. Also since there are repetitive id's this INFO propery needs to be grouped with its respective id and generate the below result

[{ID:'21',INFO:[{MARKS: 75,GRADE: 'A',RESULT: 'PASS ' },{MARKS: 32,GRADE: 'F',RESULT: 'FAIL'},{MARKS: 61,GRADE: 'B',RESULT: 'PASS']},
{ID:'87',INFO:[MARKS: 91,GRADE: 'A+',RESULT: 'PASS'],[MARKS: 72,GRADE: 'B',RESULT: 'PASS ']},
{ID:'7',INFO:{ MARKS: 100,GRADE: 'A+,RESULT: 'PASS'}
]

I am trying to use the below code but it doesn't generate the expected results

console.log(groupByField(data,'id'))
function groupByField(data, field) {
    var outObject = data.reduce(function (a, e) {

      let estKey = (e[field]);

      (a[estKey] ? a[estKey] : (a[estKey] = null || [])).push(e);
      return a;
    }, {});

    return outObject

  }

can someone help me?

monica_s
  • 65
  • 6
  • Does this answer your question? [How to group an array of objects by key](https://stackoverflow.com/questions/40774697/how-to-group-an-array-of-objects-by-key) – pilchard Sep 08 '21 at 09:55

5 Answers5

0

You can achieve the desired result using Map and forEach

const arr = [
  { ID: "21", NAME: "JACK", MARKS: 75, GRADE: "A", RESULT: "PASS " },
  { ID: "21", NAME: "JACK", MARKS: 32, GRADE: "F", RESULT: "FAIL" },
  { ID: "87", NAME: "SAM", MARKS: 91, GRADE: "A+", RESULT: "PASS" },
  { ID: "7", NAME: "TOM", MARKS: 100, GRADE: "A+", RESULT: "PASS" },
  { ID: "21", NAME: "JACK", MARKS: 61, GRADE: "B", RESULT: "PASS" },
  { ID: "87", NAME: "SAM", MARKS: 72, GRADE: "B", RESULT: "PASS " },
];

const map = new Map();
arr.forEach((o) => {
  const { ID, NAME, ...rest } = o;
  if (map.has(o.ID)) map.get(o.ID).push(rest);
  else map.set(o.ID, [rest]);
});

const result = [];
for (let [ID, INFO] of map) {
  if (INFO.length === 1) result.push({ ID, INFO: INFO[0] });
  else result.push({ ID, INFO });
}

console.log(result);
DecPK
  • 24,537
  • 6
  • 26
  • 42
0

Here is refactoring of the flagged duplicate to fit your specific needs, specifically grouping inside a nested array for each object.

It is a fairly standard 'group-by' using Array.prototype.reduce() in combination with destructuring assignment to isolate only the properties we need.

const input = [ { ID: '21', NAME: 'JACK', MARKS: 75, GRADE: 'A', RESULT: 'PASS ' }, { ID: '21', NAME: 'JACK', MARKS: 32, GRADE: 'F', RESULT: 'FAIL' }, { ID: '87', NAME: 'SAM', MARKS: 91, GRADE: 'A+', RESULT: 'PASS' }, { ID: '7', NAME: 'TOM', MARKS: 100, GRADE: 'A+', RESULT: 'PASS' }, { ID: '21', NAME: 'JACK', MARKS: 61, GRADE: 'B', RESULT: 'PASS' }, { ID: '87', NAME: 'SAM', MARKS: 72, GRADE: 'B', RESULT: 'PASS ' }, ];

const result = Object.values(
  input.reduce(function (r, { ID, NAME, ...rest }) {
    (r[ID] ??= { ID, INFO: [] }).INFO.push(rest);

    return r;
  }, Object.create(null))
);

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

The above snippet makes use of logical nullish assignment (??=). For compatibility you can use an || short-circuit instead if need be.

(r[ID] || (r[ID] = { ID, INFO: [] })).INFO.push(rest);
pilchard
  • 12,414
  • 5
  • 11
  • 23
0

function transform(info) {
  return info.reduce((pre, cur) => {
    let ext = pre.find(d => d.ID === cur.ID);
    let {ID, MARKS, GRADE, RESULT} = cur;
    if (ext) {
      ext.INFO = ext.INFO instanceof Array 
        ? [...ext.INFO, {MARKS, GRADE, RESULT}]
        : [ext.INFO, {MARKS, GRADE, RESULT}];
    } else {
      pre.push({ID, INFO:{MARKS, GRADE, RESULT}});
    }
    return pre;
  }, []);
}

Happy coding!

Silo QIAN
  • 60
  • 8
0

Using lodash groupBy() : https://lodash.com/docs/4.17.15#groupBy

const data = [
  { ID: '21', NAME: 'JACK', MARKS: 75, GRADE: 'A', RESULT: 'PASS ' },
  { ID: '21', NAME: 'JACK', MARKS: 32, GRADE: 'F', RESULT: 'FAIL' },
  { ID: '87', NAME: 'SAM', MARKS: 91, GRADE: 'A+', RESULT: 'PASS' },
  { ID: '7', NAME: 'TOM', MARKS: 100, GRADE: 'A+', RESULT: 'PASS' },
  { ID: '21', NAME: 'JACK', MARKS: 61, GRADE: 'B', RESULT: 'PASS' },
  { ID: '87', NAME: 'SAM', MARKS: 72, GRADE: 'B', RESULT: 'PASS ' },
];

const grouped = _.groupBy(data, 'NAME');

Results :

grouped:,  {
  JACK: [
    { ID: '21', NAME: 'JACK', MARKS: 75, GRADE: 'A', RESULT: 'PASS ' },
    { ID: '21', NAME: 'JACK', MARKS: 32, GRADE: 'F', RESULT: 'FAIL' },
    { ID: '21', NAME: 'JACK', MARKS: 61, GRADE: 'B', RESULT: 'PASS' }
  ],
  SAM: [
    { ID: '87', NAME: 'SAM', MARKS: 91, GRADE: 'A+', RESULT: 'PASS' },
    { ID: '87', NAME: 'SAM', MARKS: 72, GRADE: 'B', RESULT: 'PASS ' }
  ],
  TOM: [ { ID: '7', NAME: 'TOM', MARKS: 100, GRADE: 'A+', RESULT: 'PASS' } ]
}
achilleb
  • 169
  • 10
0

An approach which transforms, groups and collects items within a single reduce task and no additional iterations within each reduce step might look similar to the following one ...

function groupCollectAndTransformItemById(collector, { ID, NAME, ...rest }) {
  const { index, list } = collector;
  let infoGroup = index[ID]?.INFO

  if (Array.isArray(infoGroup)) {

    // push into an already existing grouped array.
    infoGroup.push(rest);

  } else if (infoGroup) {

    // create an array for more than just one grouped item.
    index[ID].INFO = [infoGroup, rest];
  } else {
    // create a new group-item with
    // its initial ID and first INFO data ...
    infoGroup = index[ID] = { ID, INFO: rest };

    // ... and push a reference into the collector's/
    // accumulator's `list` array which also holds
    // the final result of the reduce process.
    list.push(infoGroup);
  }
  return collector;
}
const sampleData = [
  { ID: '21',NAME: 'JACK', MARKS: 75, GRADE: 'A', RESULT: 'PASS' },
  { ID: '21',NAME: 'JACK', MARKS: 32, GRADE: 'F', RESULT: 'FAIL' },
  { ID: '87',NAME: 'SAM', MARKS: 91, GRADE: 'A+', RESULT: 'PASS' },
  { ID: '7', NAME: 'TOM', MARKS: 100, GRADE: 'A+', RESULT: 'PASS' },
  { ID: '21',NAME: 'JACK', MARKS: 61, GRADE: 'B', RESULT: 'PASS' },
  { ID: '87',NAME: 'SAM', MARKS: 72, GRADE: 'B', RESULT: 'PASS' },
];

console.log(
  sampleData
    .reduce(groupCollectAndTransformItemById, {

      index: {},
      list: [],

    }).list
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37