4

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.

Agatha
  • 43
  • 3
  • 1
    Overall what you have is not bad and it's not spaghetti code. It's easy to follow each step. There are some shortcuts like iterating Object.entries() but being able to do all this long hand as you have done is good learning experience – charlietfl May 01 '21 at 20:48
  • @charlietfl Thank You:) But I want to improve my JS-Skill and I`m totally sure that all of this can and must be written in much more simple and better way... – Agatha May 01 '21 at 20:50
  • 1
    *"Much simpler"* ... there are techniques to perhaps condense it some but then you may not find it as readable or maintainable if it's not as clear as what you currently have. One suggestion is use `forEach()` in step two instead of the more verbose `for` loop – charlietfl May 01 '21 at 20:54
  • @charlietfl Got it, thank You one more time ^-^ – Agatha May 01 '21 at 20:56
  • 1
    And read up on how `Object.keys()` , `Object.values()` and `Object.entries()` work – charlietfl May 01 '21 at 20:57
  • Does this answer your question? [How can I merge properties of two JavaScript objects dynamically?](https://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically) – Heretic Monkey May 01 '21 at 21:09
  • @HereticMonkey Not really. I`ve needed a little bit more complex thing, than simple .merge method. Anyway, thank You ^-^ – Agatha May 01 '21 at 21:15

2 Answers2

1

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);
Majed Badawi
  • 27,616
  • 4
  • 25
  • 48
1

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>
charlietfl
  • 170,828
  • 13
  • 121
  • 150