-1

I need help with converting some array to desired format in JavaScript but I couldn't know how to handle it.

Basically I need to group my data by some of the fields in object. Could you give me some advise?

Here is my data:

[
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 27,
    "source": "test template lang 27",
    "input": "2",
    "output": "2",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 27,
    "source": "test template lang 27",
    "input": "1",
    "output": "1",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 27,
    "source": "test template lang 27",
    "input": "3",
    "output": "3",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 29,
    "source": "test template of lang 29",
    "input": "2",
    "output": "2",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 29,
    "source": "test template of lang 29",
    "input": "1",
    "output": "1",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 29,
    "source": "test template of lang 29",
    "input": "3",
    "output": "3",
  },
  {
    "id": 52,
    "subId": 53,
    "title": "test title 2",
    "languageId": 22,
    "source": "test template lang 22",
    "input": "2",
    "output": "2",
  },
  {
    "id": 52,
    "subId": 53,
    "title": "test title 2",
    "languageId": 22,
    "source": "test template lang 22",
    "input": "3",
    "output": "3",
  },
  {
    "id": 52,
    "subId": 53,
    "title": "test title 2",
    "languageId": 16,
    "source": "test template lang 16",
    "input": "2",
    "output": "2",
  },
  {
    "id": 52,
    "subId": 53,
    "title": "test title 2",
    "languageId": 16,
    "source": "test template lang 16",
    "input": "3",
    "output": "3",
  }
]

and I want to output:

[
    {
        "id": 52,
        "subId": 42,
        "title": "test title",
        "lang": [
            {"languageId": 27, "source": "test template lang 27"},
            {"languageId": 29, "source": "test template of lang 29"}
        ],
        "testCases": [
            {"input": "2","output": "2"},
            {"input": "1","output": "1"},
            {"input": "3","output": "3"}
        ]
    },
        {
        "id": 52,
        "subId": 53,
        "title": "test title",
        "lang": [
            {"languageId": 22, "source": "test template lang 22"},
            {"languageId": 16, "source": "test template of lang 16"}
        ],
        "testCases": [
            {"input": "2","output": "2"},
            {"input": "2","output": "3"}
        ]
    }
]

In short I need to group this data by both lang id and testcase. Could you please help me to solve this problem?

Thank you.

technophyle
  • 7,972
  • 6
  • 29
  • 50
  • 3
    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) – VLAZ Feb 11 '20 at 08:09

2 Answers2

1

You can use the function reduce for grouping the element by a specific key and the function Object.values for getting the grouped elements as an array.

This approach assumes that the title could be any of the possible values

let arr = [  {    "id": 52,    "subId": 42,    "title": "test title",    "languageId": 27,    "source": "test template lang 27",    "input": "2",    "output": "2",  },  {    "id": 52,    "subId": 42,    "title": "test title",    "languageId": 27,    "source": "test template lang 27",    "input": "1",    "output": "1",  },  {    "id": 52,    "subId": 42,    "title": "test title",    "languageId": 27,    "source": "test template lang 27",    "input": "3",    "output": "3",  },  {    "id": 52,    "subId": 42,    "title": "test title",    "languageId": 29,    "source": "test template of lang 29",    "input": "2",    "output": "2",  },  {    "id": 52,    "subId": 42,    "title": "test title",    "languageId": 29,    "source": "test template of lang 29",    "input": "1",    "output": "1",  },  {    "id": 52,    "subId": 42,    "title": "test title",    "languageId": 29,    "source": "test template of lang 29",    "input": "3",    "output": "3",  },  {    "id": 52,    "subId": 53,    "title": "test title 2",    "languageId": 22,    "source": "test template lang 22",    "input": "2",    "output": "2",  },  {    "id": 52,    "subId": 53,    "title": "test title 2",    "languageId": 22,    "source": "test template lang 22",    "input": "3",    "output": "3",  },  {    "id": 52,    "subId": 53,    "title": "test title 2",    "languageId": 16,    "source": "test template lang 16",    "input": "2",    "output": "2",  },  {    "id": 52,    "subId": 53,    "title": "test title 2",    "languageId": 16,    "source": "test template lang 16",    "input": "3",    "output": "3",  }];

let result = Object.values(arr.reduce((r, {id, subId, title, languageId, source, input, output}) => {
  r[subId] = r[subId] || {id, subId, title, lang: [], testCases: []};
  r[subId].lang = Object.values(r[subId].lang.reduce((a, {languageId, source}) => {
    a[languageId] = {languageId, source}; 
    return a;
  }, {[languageId]: {languageId, source}}));
  
  r[subId].testCases = Object.values(r[subId].testCases.reduce((a, {input, output}) => {
    a[`${input}|${output}`] = {input, output}; 
    return a;
  }, {[`${input}|${output}`]: {input, output}}));
  return r;
}, {}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ele
  • 33,468
  • 7
  • 37
  • 75
1

You can use reduce method to group by multiple keys such as id and subId:

const result = [...data.reduce((r, o) => {    
  const key = o.id + '-' + o.subId;    
  const item = r.get(key) || { id: o.id, subId: o.subId, title: o.title, 
      lang: [], testCases: []  };
  if (!item.lang.find(f => f.languageId == o.languageId && f.source == o.source))
      item.lang.push({ languageId: o.languageId, source: o.source  });

  if (!item.testCases.find(f => f.input == o.input && f.output == o.output))
      item.testCases.push({ input: o.input, output: o.output  });

  return r.set(key, item);
}, new Map).values()];

An example:

let data = [
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 27,
    "source": "test template lang 27",
    "input": "2",
    "output": "2",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 27,
    "source": "test template lang 27",
    "input": "1",
    "output": "1",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 27,
    "source": "test template lang 27",
    "input": "3",
    "output": "3",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 29,
    "source": "test template of lang 29",
    "input": "2",
    "output": "2",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 29,
    "source": "test template of lang 29",
    "input": "1",
    "output": "1",
  },
  {
    "id": 52,
    "subId": 42,
    "title": "test title",
    "languageId": 29,
    "source": "test template of lang 29",
    "input": "3",
    "output": "3",
  },
  {
    "id": 52,
    "subId": 53,
    "title": "test title 2",
    "languageId": 22,
    "source": "test template lang 22",
    "input": "2",
    "output": "2",
  },
  {
    "id": 52,
    "subId": 53,
    "title": "test title 2",
    "languageId": 22,
    "source": "test template lang 22",
    "input": "3",
    "output": "3",
  },
  {
    "id": 52,
    "subId": 53,
    "title": "test title 2",
    "languageId": 16,
    "source": "test template lang 16",
    "input": "2",
    "output": "2",
  },
  {
    "id": 52,
    "subId": 53,
    "title": "test title 2",
    "languageId": 16,
    "source": "test template lang 16",
    "input": "3",
    "output": "3",
  }
];

const result = [...data.reduce((r, o) => {

  const key = o.id + '-' + o.subId;

  const item = r.get(key) || { id: o.id, subId: o.subId, title: o.title, 
      lang: [],   testCases: []  };
  if (!item.lang.find(f => f.languageId == o.languageId && f.source == o.source))
      item.lang.push({ languageId: o.languageId, source: o.source  });

  if (!item.testCases.find(f => f.input == o.input && f.output == o.output))
      item.testCases.push({ input: o.input, output: o.output  });

  return r.set(key, item);
}, new Map).values()];

console.log(result);
StepUp
  • 36,391
  • 15
  • 88
  • 148
  • I'm still trying to understand the reduce function in js generally, but this is very clear thank you for your answer – ahmedsalihh Feb 12 '20 at 06:29