so I have an array of values like this
const foodsList = [
{
foodOrigin: "Padang",
foodName: "Nasi Padang",
originCode: "PDN"
},
{
foodOrigin: "Padang",
foodName: "Gulai",
originCode: "PDN"
},
{
foodOrigin: "Padang",
foodName: "Rendang",
originCode: "PDN"
},
{
foodOrigin: "Palembang",
foodName: "Pempek",
originCode: "PLG"
},
{
foodOrigin: "Palembang",
foodName: "Tekwan",
originCode: "PLG"
},
{
foodOrigin: "Yogyakarta",
foodName: "Gudeg",
originCode: "YKT"
}
];
and I want to filter the array so the result would be like this instead
const filteredFoodsList = [
{
foodOrigin: "Padang",
originCode: "PDN"
},
{
foodOrigin: "Palembang",
originCode: "PLG"
},
{
foodOrigin: "Yogyakarta",
originCode: "YKT"
}
];
In order to achieve the result, I tried doing it like below but is there a cleaner and better way to do this? ( especially since my actual array's data consist of more than 500 )
const filteredFoodsList = [];
for (let i = 0; i < foodsList.length; i++) {
if (i === 0) {
filteredFoodsList.push({
originCode: foodsList[i].originCode,
foodOrigin: foodsList[i].foodOrigin
});
}
let isExist = false;
for (let j = 0; j < filteredFoodsList.length; j++) {
if (foodsList[i].originCode === filteredFoodsList[j].originCode) {
isExist = true;
}
}
if (!isExist) {
filteredFoodsList.push({
originCode: foodsList[i].originCode,
foodOrigin: foodsList[i].foodOrigin
});
}
}
As the OP stated there should be some focus on performance, so..
The Map
could be the fastest way to collect unique values.
(I've borrowed Array.from(map, ([originCode, foodOrigin]) => ({ foodOrigin, originCode }))
from Nina's answer, really elegant).
But it doesn't affect the performance since the resulting array should be quite small.
But she forgot to include a Map::has()
check that really improves the performance significantly.
What's important is how fast the source array is iterated. As always I see there can't be a faster thing than for(let i = 0; ...)
...
const foodsList=[{foodOrigin:"Padang",foodName:"Gulai",originCode:"PDN"},{foodOrigin:"Padang",foodName:"Rendang",originCode:"PDN"},{foodOrigin:"Palembang",foodName:"Pempek",originCode:"PLG"},{foodOrigin:"Palembang",foodName:"Tekwan",originCode:"PLG"},{foodOrigin:"Yogyakarta",foodName:"Gudeg",originCode:"YKT"}];
const map = new Map;
for (let i = 0; i < foodsList.length; i++) {
const item = foodsList[i];
map.has(item.originCode) || map.set(item.originCode, item.foodOrigin);
}
const result = Array.from(map, ([originCode, foodOrigin]) => ({ foodOrigin, originCode }));
console.log(result);
And a benchmark:
` Chrome/121
--------------------------------------------------------------------------------------
> n=10000 | n=100000 | n=1000000 | n=10000000
Alexander's solution 1.23x x10k 526 | 1.00x x1k 518 | 1.00x x100 543 | 1.00x x10 554
Nina's improved 1.00x x10k 429 | 1.35x x1k 700 | 1.32x x100 717 | 1.31x x10 727
Peter's solution 1.00x x10k 427 | 1.44x x1k 746 | 1.35x x100 732 | 1.37x x10 760
Nina's solution 1.23x x10k 524 | 1.55x x1k 801 | 1.50x x100 815 | 1.46x x10 811
--------------------------------------------------------------------------------------
https://github.com/silentmantra/benchmark `
` Firefox/122
---------------------------------------------------------------------------------------
> n=10000 | n=100000 | n=1000000 | n=10000000
Alexander's solution 1.00x x10k 355 | 1.00x x1k 363 | 1.00x x100 383 | 1.00x x10 380
Nina's improved 3.63x x1k 129 | 3.64x x100 132 | 3.86x x10 148 | 3.68x x1 140
Peter's solution 4.00x x1k 142 | 3.55x x100 129 | 3.39x x10 130 | 3.95x x1 150
Nina's solution 4.08x x1k 145 | 4.16x x100 151 | 3.97x x10 152 | 4.03x x1 153
---------------------------------------------------------------------------------------
https://github.com/silentmantra/benchmark `
<script>
const chunk = [{ foodOrigin: "Padang", foodName: "Gulai", originCode: "PDN" }, { foodOrigin: "Padang", foodName: "Rendang", originCode: "PDN" }, { foodOrigin: "Palembang", foodName: "Pempek", originCode: "PLG" }, { foodOrigin: "Palembang", foodName: "Tekwan", originCode: "PLG" }, { foodOrigin: "Yogyakarta", foodName: "Gudeg", originCode: "YKT" }];
const $chunk = [];
let count = 2000;
while (count--) {
$chunk.push(...chunk);
}
const $input = [];
const foodsList = $input;
// @benchmark Peter's solution
foodsList
.reduce((collector, { foodOrigin, originCode = null }) => {
if (originCode !== null && !collector.lookup.has(foodOrigin)) {
collector.lookup.set(foodOrigin, true);
collector.result.push({
foodOrigin,
originCode,
});
}
return collector;
}, { lookup: new Map, result: [] }).result;
// @benchmark Nina's solution
Array.from(
foodsList.reduce((m, { foodOrigin, originCode }) => m.set(foodOrigin, originCode), new Map),
([foodOrigin, originCode]) => ({ foodOrigin, originCode })
);
// @benchmark Alexander's solution
const map = new Map;
for (let i = 0; i < foodsList.length; i++) {
const item = foodsList[i];
map.has(item.originCode) || map.set(item.originCode, item.foodOrigin);
}
Array.from(map, ([originCode, foodOrigin]) => ({ foodOrigin, originCode }));
// @benchmark Nina's improved
Array.from(
foodsList.reduce((m, { foodOrigin, originCode }) => {
m.has(foodOrigin) || m.set(foodOrigin, originCode);
return m;
}, new Map),
([foodOrigin, originCode]) => ({ foodOrigin, originCode })
);
</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>