Reduce and Filter JavaScript Object on Property

How do you filter a JavaScript object that has duplicate properties? Use reduce! The specific situation I had is a list of objects with duplicate properties. I wanted to filter out the duplicates and return a new object with only the unique properties.
There's two sections here, the first is how I approached the solution then, there's the much simpler approach which I have now adopted.
What the data looks like
Here's a small example of what the object looked like:
[
{
"productId": "1",
"slug": "laptop",
"productName": "Laptop",
"variant": "16gb"
},
{
"productId": "1",
"slug": "laptop",
"productName": "Laptop",
"variant": "32gb"
},
{
"productId": "2",
"slug": "tablet",
"productName": "Tablet",
"variant": "32gb"
},
{
"productId": "2",
"slug": "tablet",
"productName": "Tablet",
"variant": "128gb"
}
]
I'd be using the different variants on another part of the project so
they weren't really relevant here. All I really needed was the
productName and some other details that I've not added to the
example object here.
A lot of the examples you'll see on using reduce will be to count up a value, in my case I needed to have only the relevant information to add back into an object to build out the page details.
A mentor of mine, Leigh Halliday has done some great videos detailing how to use reduce.
If you want a really good explanation of how to use filter, map and reduce go check out Leigh's videos!
This is another good one brought to my attention by Joost Schuur on Twitter.
My approach
So, my specific use-case! I'll first set up the .reduce function
with the accumulator (the thing I'm adding to) and the current
item in the reduce, then return the acc which is what I want at
the end of the reduce.
Then finally add the initial value of the reduce which I want
returned, in this case it's an array [].
items.reduce((acc, item) => {
return acc
}, [])
Great I get an empty array back. So now I need to start adding items
to the acc.
I'll set up a check to see if the item.productId is already in the
acc and if it's not add it to the acc an if statement adding the
item to the acc.
Then I'll add the matching item for the productId to the acc.
items.reduce((acc, item) => {
if (!acc[item.productId]) {
acc[item] = item
}
acc[item.productId] = item
return acc
}, [])
There's an empty slot in the acc for the productId so I'll add a
.filter to the end of the .reduce so that any items that are
null are removed from the acc.
Here's the finished code:
const products = items
.reduce((acc, item) => {
if (!acc[item.productId]) {
acc[item] = item
}
acc[item.productId] = item
return acc
}, [])
.filter(item => item !== null)
Leigh's approach
After running my approach past Leigh, he had a much simpler solution!
He had a small suggestion on the final code... the initial value
should probably be an object {} so that I can check if the
productId exists inside of that object.
What I'd get at the end though would be an object where the keys are
the product ids... so if I just want the deduped values, I could use
Object.values() to extract them.
const dedupedObject = items.reduce((acc, item) => {
if (!acc[item.productId]) {
acc[item.productId] = item
}
return acc
}, {})
const dedupedArray = Object.values(dedupedObject)
I could even go a step further and just remove the if statement, since it would just override the previous product with the same product id:
const dedupedObject = items.reduce((acc, item) => {
acc[item.productId] = item
return acc
}, {})
const dedupedArray = Object.values(dedupedObject)
A lot more simpler than my solution! Thanks Leigh!
Fin!
That's it! Hope you found it useful!



