Reputation: 1415
I have this piece of code:
this.serverlist = data.NodeList.map((a) => {
if (a.productTypeId === "1") {
return a.HostName;
}
});
this.serverlist = this.serverlist.filter((x) => {
return x !== undefined;
});
And I want to replace this 2 statements(.map
& .filter
) with .reduce
. How do I do that?
Upvotes: 1
Views: 3137
Reputation: 155045
And I want to replace this 2 statements(
.map
&.filter
) with.reduce
. How do I do that?
You can't[1] and you shouldn't.
The .reduce
function is for when you want to reduce the entire collection to a new single value (or object) such as when you're aggregating values (e.g. SUM, Count, etc) or populating a Set<T>
... which is not what you're doing here.
Instead just chain filter
and map
together in that order: use filter
first to eliminate the possibility of encountering undefined
or invalid input, then use map
to extract the gubbins, then followed by sort
for good-measure:
Like so:
this.serverList = data.NodeList
.filter( a => a.productTypeId === '1' && typeof a.HostName === 'string' )
.map( a => a.hostName )
.sort();
Note that .filter
and .map
are FP-style and so don't modify the input data.NodeList
array at all and instead always return new Array
objects' whereas the .sort()
mutates the array in-place (but still returns it).
Footnote 1:
I wrote "you can't" to discourage people from misusing (IMHO) JS's Array's FP-style functions, which are far easier for a JS engine's JIT to optimize when used as stateless pure functions.
I'll admit that technically reduce
can still be used here, but it can only work when using reduce
as a substitute for for(of)
(or .forEach
), but which requires you to avoid invalid input by adding conditional logic (which cannot be optimized as easily by the JIT compared to using map
and filter
as pure FP functions) which kinda defeats the point of using FP-style functions (again, IMO).
For shits and giggles, I ended up writing a .reduce
version, benchmarked here (props to @dancrumb for the challenge) and (to my surprise) my .reduce
version ("reduce-only-v2
" in that jsPerf benchmark) is actually faster than my filter+map
version (at least in Chrome 129 x64 on Windows 10) which suggests that Chrome's JIT does not yet apply parallel-computing optimizations to Array's FP functions - but I'll maintain that the filter+map
approach is still preferable to .reduce
because it's easier to reason about at-a-glance and also because eventually (I hope!) browsers' JITs will be able to optimize filter+map
better for many-core processors than .reduce
can:
this.serverList = data.NodeList.reduce( ( acc, a ) => {
if( ( a.productTypeId === '1' ) && ( typeof a.HostName === 'string' ) ) {
acc.push( a.hostName ); // `Array.prototype.push` returns `.length`, so `return acc.push(..);` won't work here.
return acc;
}
return acc;
}, /*seed:*/ [] )
and for fun, let's use everyone's favourite JS operator to reduce har-har it down to a 1-liner:
this.serverList = data.NodeList.reduce( ( acc, val ) => ( a.productTypeId === '1' && typeof a.HostName === 'string' ) ? ( acc.push( a.hostName ), acc ) : acc, /*seed:*/ [] );
...which runs in the same time as the above, but will annoy your coworkers.
Upvotes: 3
Reputation: 14881
I could understand your snippet as
const NodeList = [
{ productTypeId: "1", HostName: "abc.com" },
{ productTypeId: "2", HostName: "abc.com" },
{ productTypeId: "1" },
{ productTypeId: "1", HostName: "xyz.com" },
]
let serverlist = NodeList.map(a => {
if (a.productTypeId === "1") {
return a.HostName
}
})
serverlist = serverlist.filter(x => {
return x !== undefined
})
console.log(serverlist)
// [ 'abc.com', 'xyz.com' ]
So you could combine to use reduce like this, do filter and get relevant pieces of data in one go
const NodeList = [
{ productTypeId: "1", HostName: "abc.com" },
{ productTypeId: "2", HostName: "abc.com" },
{ productTypeId: "1" },
{ productTypeId: "1", HostName: "xyz.com" },
]
const serverlist = NodeList.reduce((acc, el) => {
if (el.productTypeId === "1" && el.HostName) {
acc.push(el.HostName)
}
return acc
}, [])
console.log(serverlist)
Upvotes: 1