Agatha
Agatha

Reputation: 43

Create new object with unique elements from objects of array of objects

I have an Array of Objects. Every object in this Array has some Keypairs. One of this Keypairs ("obj", for example) is an Array of Objects too.

Example what I have:

const arrOfObj = [
  { 
    "id": 1
    "obj": {
      "arr1": ["arr1-1"],
      "arr2": ["arr2-1", "arr2-2"],
      "arr3": ["arr3-1", "arr3-2"]
    }
  },
  { 
    "id": 1
    "obj": {
      "arr1": ["arr1-2"],
      "arr2": ["arr2-1", "arr2-3"],
      "arr3": ["arr3-1", "arr3-3"],
      "arr4": ["arr4-1"],
    }
  },
];

I need to get new Object of "obj" Objects with unique keys and unique elements inside them.

Example what I need:

const newObj = {
   "arr1": ["arr1-1", "arr1-2"],
   "arr2": ["arr2-1", "arr2-2", "arr2-3"],
   "arr3": ["arr3-1", "arr3-2", "arr3-3"],
   "arr4": ["arr4-1"],
}

All of this comes dynamically from API by request, so I don`t know the names of this keypairs, but i need to store them.

I have Solution, but I`m new in JavaScript, and want to know how to simplify and improve my poor Code.

1. First, I`m defining the new Object and retrieving the Names for his keypairs from "arrOfObj".

let filterObj = {};

arrOfObj.forEach(function (item) {
  for (let key in item.obj) {
    filterObj[key] = [];
  }
});

2. After that I`m getting all the Elements of every Array from "arrOfObj" and store them in new Object "filterObj" in the Keypair with the same Name.

arrOfObj.forEach(function (item) {
  for (let key in item.obj) {
    for (let element = 0; element < item.obj[key].length; element++) {
      filterObj[key].push(item.obj[key][element]);
    }
  }
});

3. To the end I`m filtering Arrays to get unique Elements only.

for (let key in filterObj) {
  filterObj[key] = Array.from(new Set(filterObj[key]));
}

It works, I`ve got what I want, but it seems to much monstrously. How this code can be simplified the best way?

Thanks for the help and advices.

Upvotes: 4

Views: 955

Answers (2)

charlietfl
charlietfl

Reputation: 171698

You can use some destructuring and Object.entries() and Object.keys() to streamline this and do everything to the new Object only

const newObj  = {}

arrOfObj.forEach(({obj}) => {
    Object.entries(obj).forEach(([k, arr]) => {
       newObj[k] = newObj[k] || [];
       newObj[k].push(...arr);
    })
});

Object.keys(newObj).forEach(k => newObj[k] = [...new Set(newObj[k])]);

console.log(newObj)
<script>
const arrOfObj=[{id:1,obj:{arr1:["arr1-1"],arr2:["arr2-1","arr2-2"],arr3:["arr3-1","arr3-2"]}},{id:1,obj:{arr1:["arr1-2"],arr2:["arr2-1","arr2-3"],arr3:["arr3-1","arr3-3"],arr4:["arr4-1"]}}];
</script>

Upvotes: 1

Majed Badawi
Majed Badawi

Reputation: 28434

Another solution using Object#fromEntries, Array#reduce, Object#entries, Array#forEach, Set, and Map:

const arrOfObj = [ { "id": 1, "obj": { "arr1": ["arr1-1"], "arr2": ["arr2-1", "arr2-2"], "arr3": ["arr3-1", "arr3-2"] } }, { "id": 1, "obj": { "arr1": ["arr1-2"], "arr2": ["arr2-1", "arr2-3"], "arr3": ["arr3-1", "arr3-3"], "arr4": ["arr4-1"] } } ];

const filterObj = 
  // transform the resulting list of key-values pairs to an object at the end
  Object.fromEntries(
    // get a map of array name as key and its unique items as value
    [...arrOfObj.reduce((map, { obj = {} }) => {
      // iterate over current element's object to update the map
      Object.entries(obj).forEach(([currentKey, currentValues]) => {
        const keyValues = [...(map.get(currentKey) || []), ...currentValues];
        map.set(currentKey, [...new Set(keyValues)]);
      });
      return map;
    }, new Map)]
  );

console.log(filterObj);

Upvotes: 1

Related Questions