I am dealing with a couple of arrays of objects which will get rendered into the UI using React. So here's the context of what I am doing. I get different data sets from different APIs. These datasets are arrays of arrays of objects. E.g
[
[{age: 23, name: john}],
[{age: 24, name: jane}],
[{age: 25, name: sam}],
[{age: 26, name: smith}],
]
With this format, it will be hard to render the result in the UI. So a simple fix will be to flatten the array which returns a single array of objects which then I can map through and display the data on the UI. Now, I have this kind of operation for each of the datasets returned from the API.
array.flat()
array.filter()
array.sort()
array.concat()
Following this style I listed above seems to be imperative to me, I wanted to use a more functional approach where I can pipe
functions together, which will make it more declarative rather than imperative. Currently, I am stuck on writing a function that will accept arrays as inputs, then flatten the arrays into a single final array. Now I know I can flatten an array using the reduce()
method and. I came across a solution on StackOverflow which looks like this and can be seen from this link Flatten Array
const flatten = (arr) => {
return arr.reduce((flat, toFlatten) => {
return flat.concat(
Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten
)
}, [])
}
I was thinking maybe I could spread the input arr
parameter but that seems to throw this error
RangeError: Maximum call stack size exceeded
I would appreciate it if I could be guided in the right way and also shown an efficient way to achieve all these operations I listed out. Thank you
This solution should do what you're looking for. It would help to have a few samples of different return data to merge together, but I tried to reproduce what you explained in my solution below.
First I combine all the different source arrays into one single array and then flatten that to any depth any of the source arrays have. In case this depth is variable, I used flat(Infinity)
but you could use a finite depth as well (flat(2)
or flat(3)
). Once I've flattened the array, I briefly convert it to a Set
to remove any duplicates and then convert it back to an array.
const source1 = [
[{age: 23, name: 'john'}],
[{age: 24, name: 'jane'}],
[{age: 25, name: 'sam'}],
[{age: 26, name: 'smith'}]
];
const source2 = [
[{age: 27, name: 'mike'}],
[{age: 28, name: 'joanne'}],
[{age: 29, name: 'lois'}],
[{age: 30, name: 'paul'}]
];
const allSources = [...new Set([source1,source2].flat(Infinity))];
console.log(allSources);
** Important note here: Because arrays and objects are reference types, even if two source arrays have the same object by key-value pairs, it won't actually be seen as a duplicate. In order to truly filter out any duplicate objects, you'll need to loop through the keys for both objects and compare their associated values, like this:
const source1 = [
[{age: 23, name: 'john'}],
[{age: 24, name: 'jane'}],
[{age: 25, name: 'sam'}],
[{age: 26, name: 'smith'}]
];
const source2 = [
[{age: 23, name: 'john'}],
[{age: 24, name: 'jane'}],
[{age: 25, name: 'sam'}],
[{age: 26, name: 'smith'}],
[{age: 27, name: 'mike'}],
[{age: 28, name: 'joanne'}],
[{age: 29, name: 'lois'}],
[{age: 30, name: 'paul'}]
];
const allSources = [source1,source2].flat(Infinity).filter((obj,_,a) => a.find(o => Object.keys(obj).length === Object.keys(o).length && Object.keys(obj).every(objKey => Object.keys(o).includes(objKey) && obj[objKey] === o[objKey])) === obj);
console.log(allSources);