I'm looking for an elegant way (immutable preferably, lodash - ok) to conditionally compact an array.
I want to check duplicates of combination of section and path values, and append ids of those duplicates.
So given
const arrayToBeCompacted = [
{
section: "Section name 1",
path: ["segment1", "segment2"],
ids: ["id1"]
},
{
section: "Section name 1",
path: ["segment1", "segment2"],
ids: ["id2"]
},
{
section: "Section name2",
path: ["segment1", "segment2"],
ids: ["id3"]
},
{
section: "Another section",
path: ["segment1", "segment2"],
ids: ["id4"]
},
{
section: "Another section",
path: ["segment1", "segment2"],
ids: ["id5"]
},
]
As a result I'd like
const resultingArray = [
{
section: "Section name 1",
path: ["segment1", "segment2"],
ids: ["id1", "id2"]
},
{
section: "Section name 2",
path: ["segment1", "segment2"],
ids: ["id3"]
},
{
section: "Another section",
path: ["segment1", "segment2"],
ids: ["id4", "id5"]
},
]
Seems that lodash.uniq(By, With) and lodash.union(By, With) are not exactly what I'm looking for.
You can do it with reduce
const arrayToBeCompacted = [
{
section: "Section name 1",
path: ["segment1", "segment2"],
ids: ["id1"]
},
{
section: "Section name 1",
path: ["segment1", "segment2"],
ids: ["id2"]
},
{
section: "Section name2",
path: ["segment1", "segment2"],
ids: ["id3"]
},
{
section: "Another section",
path: ["segment1", "segment2"],
ids: ["id4"]
},
{
section: "Another section",
path: ["segment1", "segment2"],
ids: ["id5"]
},
]
const compacted = Object.values(arrayToBeCompacted.reduce((res, {section, path, ids}) => {
const key = section + path.join('|')
const existing = res[key] || {ids: [], section, path}
existing.ids = [...existing.ids, ...ids]
res[key] = existing
return res
}, {}))
console.log(compacted)
Below is one possible way to achieve the target.
Code Snippet
// method to compact array of objects
const compactArr = arr => (
Object.values( // extract values from intermediate result-obj
arr.reduce( // iterate over the array using "reduce"
(acc, itm) => { // "acc" is the accumuator/aggregator
const {section, ids, path} = itm; // de-structure to get props
// construct uniq-key using "section" and "path"
const uniqKey = `${section}-${path.join('-')}`;
acc[uniqKey] = {
...(acc[uniqKey] || {}),
section, path, // if "section" already exists, concat "ids"
ids: (acc[uniqKey]?.ids ?? []).concat(ids)
}
return acc;
},
{} // initial value of "acc" is empty-object {}
)
) // implicit return from method
);
const arrayToBeCompacted = [{
section: "Section name 1",
path: ["segment1", "segment2"],
ids: ["id1"]
},
{
section: "Section name 1",
path: ["segment1", "segment2"],
ids: ["id2"]
},
{
section: "Section name2",
path: ["segment1", "segment2"],
ids: ["id3"]
},
{
section: "Another section",
path: ["segment1", "segment2"],
ids: ["id4"]
},
{
section: "Another section",
path: ["segment1", "segment2"],
ids: ["id5"]
},
];
console.log('compacted array: ', compactArr(arrayToBeCompacted));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added in the snippet above.