(N.b. pilchard's implementation is very similar to mine, but I think that the performance caveat is a good complement to that answer — so I'll post anyway.)
I think that your implementation is both simple and elegant, but here is an alternative implementation that replaces the quadratic scanning with an intermediate accumulator:
let acc = {};
countries.forEach((({name, id}) => acc[name] = {id, name, networks: []}))
networks.forEach(({id, name, country}) => acc[country].networks.push({id, name}));
let newState = Object.values(acc).filter(x => x.networks.length);
Caveat:
Don't be afraid of expressing business logic using nested loops, it is usually fast enough given every day data – and often pretty easy to read. Out of our four implementations, your initial one runs — by far — the fastest on my machine*. (Things change with data set size and runtime environment, so take these figures with a grain of salt.)
*) Node v14.5.0 on a 13" MacBook Pro (2017)
Yana's dataset:
- Yana's x 4,828,934 ops/sec ±0.91% (89 runs sampled)
- Folkol's x 1,861,775 ops/sec ±0.46% (95 runs sampled)
- Muljeeb's x 396,587 ops/sec ±1.32% (89 runs sampled)
- Pilchard's x 195,126 ops/sec ±0.93% (91 runs sampled)
Slightly larger dataset:
- Folkol's x 1,920 ops/sec ±0.95% (93 runs sampled)
- Pilchard's x 53.12 ops/sec ±1.38% (68 runs sampled)
- Yana's x 21.48 ops/sec ±12.75% (46 runs sampled)
- Muljeeb's x 16.06 ops/sec ±5.68% (44 runs sampled)
Here is the test code (using benchmark.js), if you want to reproduce the test results on your machine:
let {Suite} = require('benchmark')
const networks = [
{
id: 1,
name: "royal warmth",
country: "Netherlands"
},
{
id: 2,
name: "interested power",
country: "United Kingdom"
},
{
id: 3,
name: "drunk prejudice NL",
country: "Netherlands"
},
{
id: 10,
name: "small Media promotion FR",
country: "France"
},
{
id: 11,
name: "experimental vat FR",
country: "France"
},
]
const countries = [
{
id: 122,
name: "Afghanistan"
},
{
id: 190,
name: "France"
},
{
id: 210,
name: "Netherlands"
},
{
id: 226,
name: "United Kingdom"
}
]
const num_countries = 195;
const networks_per_country = 100;
const many_countries = [...Array(num_countries).keys()].map(n => ({
id: n,
name: `country_${n}`
}))
const many_networks = [...Array(num_countries * networks_per_country).keys()].map(n => ({
id: n,
name: `network_${n}`,
country: `country_${n % networks_per_country}`
}));
function merge_yana(countries, networks) {
let newState = [];
countries.forEach((country) => {
let newNetworks = [];
networks.forEach((network) => {
if (network.country === country.name) {
newNetworks.push({
id: network.id,
name: network.name,
})
}
});
if (newNetworks.length !== 0) {
newState.push({
id: country.id,
name: country.name,
networks: newNetworks,
})
}
});
return newState;
}
function merge_pilchard(countries, networks) {
const networkMap = new Map;
for (const {country, ...network} of networks) {
networkMap.set(country, [...(networkMap.get(country) ?? []), network]);
}
const newState = [];
for (const country of countries) {
if (networkMap.has(country.name)) {
newState.push({
...country,
networks: networkMap.get(country.name)
});
}
}
return newState;
}
function merge_muljeeb(countries, networks) {
const newState = countries.map(country => (networks.filter(e => e.country == country.name).length > 0 && {
...country,
networks: networks.filter(e => e.country == country.name)
})).filter(e => e !== false)
return newState;
}
function merge_folkol(countries, networks) {
let acc = {};
countries.forEach((({name, id}) => acc[name] = {id, name, networks: []}))
networks.forEach(({id, name, country}) => acc[country].networks.push({id, name}));
return Object.values(acc).filter(x => x.networks.length);
}
function benchmark(name, countries, networks) {
console.log(name);
Suite(name)
.add("Yana's", () => merge_yana(countries, networks))
.add("Pilchard's", () => merge_pilchard(countries, networks))
.add("Muljeeb's", () => merge_muljeeb(countries, networks))
.add("Folkol's", () => merge_folkol(countries, networks))
.on('cycle', ({target}) => console.log(` - ${target}`))
.run();
}
benchmark("Yana's dataset:", countries, networks);
benchmark("Slightly larger dataset:", many_countries, many_networks);