1

I have this,

var o = [{
  "id": 1, // its actually a string in real life
  "course": "name1",
  // more properties
}, 
{
  "id": 1, // its actually a string in real life
  "course": "name2",
  // more properties
}];

I want this,

var r = [{
  "id": 1, // its actually a string in real life
  "course": ["name1", "name2"],
}];

I am trying this,

var flattened = [];
for (var i = 0; i < a.length; ++i) {
  var current = a[i];
  if(flattened.)
}

but I am stuck, I am not sure what to do next, array will have more then 2 records but this was just an example.

THERE are more fields but I removed them for simplicity, I won't be using them in final array.

Mathematics
  • 7,314
  • 25
  • 77
  • 152

6 Answers6

4

You could reduce the array and find the object.

var array = [{ id: 1, course: "name1" }, { id: 1, course: "name2" }],
    flat = array.reduce((r, { id, course }) => {
        var temp = r.find(o => id === o.id);
        if (!temp) {
            r.push(temp = { id, course: [] });
        }
        temp.course.push(course);
        return r;
    }, []);

console.log(flat);

The same by taking a Map.

var array = [{ id: 1, course: "name1" }, { id: 1, course: "name2" }],
    flat = Array.from(
        array.reduce((m, { id, course }) => m.set(id, [...(m.get(id) || []) , course]), new Map),
        ([id, course]) => ({ id, course })
    );

console.log(flat);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

This way you will get the data flattened in the shape you want

const o = [
  {
    id: 1,
    course: "name1"
  },
  {
    id: 1,
    course: "name2"
  },
  {
    id: 2,
    course: "name2"
  }
];

const r = o.reduce((acc, current) => {
  const index = acc.findIndex(x => x.id === current.id);
  if (index !== -1) {
    acc[index].course.push(current.course);
  } else {
    acc.push({id:current.id, course: [current.course]});
  }
  return acc
}, []);

console.log(r);
Prithwee Das
  • 4,628
  • 2
  • 16
  • 28
1

You could use .reduce to create an object of keys, and then use that object to set keys to be of the id. This way you can add to the same course array by targetting the id of the object. Lastly, you can get the values of the object to get your result.

See example below:

var o = [{
  "id": 1,
  "course": "name1",
  "foo": 1
}, 
{
  "id": 1,
  "course": "name2",
  "bar": 2
}];

var res = Object.values(o.reduce((acc, {id, course, ...rest}) => {
  if(id in acc) 
    acc[id] = {...acc[id], course: [...acc[id].course, course], ...rest};
  else acc[id] = {id, course: [course], ...rest};
  return acc;
}, {}));

console.log(res);
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
1

You can do this with reduce and Object.entries. This example works for any number of properties:

const o = [
  { id: 1, course: 'name1', time: 'morning', topic: 'math' },
  { id: 1, course: 'name2', time: 'afternoon' },
  { id: 2, course: 'name3', time: 'evening' }
];

const result = o.reduce((out, { id, ...rest }) => {
  out[id] = out[id] || {};
  const mergedProps = Object.entries(rest).reduce((acc, [k, v]) => {
    return { ...acc, [k]: [...(out[id][k] || []), v] };
  }, out[id]);
  out[id] = { id, ...mergedProps };
  return out;
}, {});

console.log(result);

If you only care about the id and course fields, you can simplify to this:

const o = [
  { id: 1, course: 'name1', time: 'morning', topic: 'math' },
  { id: 1, course: 'name2', time: 'afternoon' },
  { id: 2, course: 'name3', time: 'evening' }
];

const result = o.reduce((out, { id, course }) =>
  ({ ...out, [id]: { id, course: [...((out[id] || {}).course || []), course] } })
, {});

console.log(result);
jo_va
  • 13,504
  • 3
  • 23
  • 47
0

You can use reduce to accumulate the results. Search in the current result (accumulator a) for an object (el) with the same id, if found, append course to existing object and return the same accumulator, otherwise put into the accumulator with course as an array.

var res = o.reduce((a, {id,course}) => {
    var found = a.find(el => el.id == id);
    return found ? found.course.push(course) && a : [...a, {id, course: [course]}];
}, []);
Vadim
  • 8,701
  • 4
  • 43
  • 50
ttulka
  • 10,309
  • 7
  • 41
  • 52
0

function merge(array, key = 'id') {
  const obj = {}
  
  for(const item of array) {
    const existing = obj[item[key]]
    if(existing) {
      for(const [name, value] of Object.entries(item)) {
        if(name === key) continue;
        
        if(existing[name]) {
          existing[name] = [ ...(existing[name].$custom ? existing[name] : [existing[name]]), value ]
          existing[name].$custom = true;
        } else {
          existing[name] = value;          
        }
      }
    } else {
      obj[item[key]] = { ...item }
    }
  }
  
  return Object.values(obj)
}

var o = [
{
  "id": 1,
  "single": "test"
},
{
  "id": 1,
  "course": "name1",
  "multifield": "test"
}, 
{
  "id": 1,
  "course": "name2"
},
{
  "id": 1,
  "newfield": "test"
}, {
  "id": 2,
  "anotherid": "test",
  "array": [1,3,4]
}, {
  "id": 2,
  "array": "text"
}];

console.log(merge(o))
AlexOwl
  • 869
  • 5
  • 11
  • My solution benefits are 1. You can specify custom id key 2. Not merged properties are not arrays – AlexOwl Mar 12 '19 at 12:18