ajyvardan
ajyvardan

Reputation: 125

Reduce multi-dimensional array of objects into a single array of object

I have this multi dimensional array of objects -

const initialArray = [
  {
   name: 'aaa', 
   value:[{id:1, data:1}, {id:2, data:2}, {id:3, data:3}]
  },
  {
   name: 'bbb', 
   value:[{id:1, data:4}, {id:2, data:5}, {id:3, data:6}]
  },
  {
   name: 'ccc', 
   value:[{id:1, data:7}, {id:2, data:8}, {id:3, data:7}]
  },
  {
   name: 'ddd', 
   value:[{id:1, data:2}, {id:2, data:1}, {id:3, data:1}]
  }
]

I need to convert it into this array -

const finalArray = [
  {
    id: 1, aaa: 1, bbb: 4, ccc: 7, ddd: 2
  },
  {
    id: 2, aaa: 2, bbb: 5, ccc: 8, ddd: 1
  },
  {
    id: 3, aaa: 3, bbb: 6, ccc: 7, ddd: 1
  }
]

The solution I have right now is

initialArray.reduce((accum, arrayData) => {
  arrayData.value.forEach(element => {
    if (accum.some(el => el.id === element.id)) {
      accum.find(el => el.id === element.id)[arrayData.name] = element.data;
    } else {
        accum.push({ id: element.id, [arrayData.name]: element.data });
    }
  });
  return accum;
}, []);

Is there any better and more elegant way to do this? I'm trying to accomplish this by reducing the number of times I iterate through each array and with lesser code and more readability. As you can see, the some and find during each iteration increases the number of iterations. Is there any way to reduce it?

Upvotes: 2

Views: 1532

Answers (3)

user1636522
user1636522

Reputation:

Here is what looks readable to me (note that I voted to close your question because "primarily opinion-based"):

initial = [{ 
  name : "aaa",
  value : [
    { id : 1, data : 1 },
    { id : 2, data : 2 },
    { id : 3, data : 3 }
  ]
}, {
  name : "bbb",
  value : [
    { id : 1, data : 4 },
    { id : 2, data : 5 },
    { id : 3, data : 6 }
  ]
}, {
  name : "ccc",
  value : [
    { id : 1, data : 7 },
    { id : 2, data : 8 },
    { id : 3, data : 7 }
  ]
}, {
  name : "ddd",
  value : [
    { id : 1, data : 2 },
    { id : 2, data : 1 },
    { id : 3, data : 1 }
  ]
}];

map = {};
final = [];

for (i = 0; i < initial.length; i++) {
  x = initial[i];
  for (j = 0; j < x.value.length; j++) {
    y = x.value[j];
    if (!map[y.id]) map[y.id] = { id: y.id };
    map[y.id][x.name] = y.data;
  }
}

for (k in map) {
  final.push(map[k]);
}

for (i = 0; i < final.length; i++) {
  console.log(JSON.stringify(final[i]));
}

Upvotes: 0

AlexOwl
AlexOwl

Reputation: 867

function tranform(array) {
  const obj = {}
  
  array.forEach(({name, value}) => (value || []).forEach(({id, data}) => obj[id] = { id, ...obj[id], [name]: data } ))
  
  return Object.values(obj)
}

const initialArray = [
  {
   name: 'aaa', 
   value:[{id:1, data:1}, {id:2, data:2}, {id:3, data:3}]
  },
  {
   name: 'bbb', 
   value:[{id:1, data:4}, {id:2, data:5}, {id:3, data:6}]
  },
  {
   name: 'ccc', 
   value:[{id:1, data:7}, {id:2, data:8}, {id:3, data:7}]
  },
  {
   name: 'ddd', 
   value:[{id:1, data:2}, {id:2, data:1}, {id:3, data:1}]
  }
]

console.log(tranform(initialArray))

Upvotes: 5

Nina Scholz
Nina Scholz

Reputation: 386550

You could take a Map for collecting objects with same id.

var initialArray = [{ name: 'aaa', value: [{ id: 1, data: 1 }, { id: 2, data: 2 }, { id: 3, data: 3 }] }, { name: 'bbb', value: [{ id: 1, data: 4 }, { id: 2, data: 5 }, { id: 3, data: 6 }] }, { name: 'ccc', value: [{ id: 1, data: 7 }, { id: 2, data: 8 }, { id: 3, data: 7 }] }, { name: 'ddd', value: [{ id: 1, data: 2 }, { id: 2, data: 1 }, { id: 3, data: 1 }] }],
    result = Array.from(initialArray
        .reduce(
            (m, { name, value }) => value.reduce(
                (n, { id, data }) => n.set(id, Object.assign(
                    n.get(id) || { id },
                    { [name]: data }
                )),
                m
            ),
            new Map
        )
        .values()
    );

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

Upvotes: 1

Related Questions