Set keyname with spread operator - javascript

Is it possible to dynamically set key name to spread operator?
For example I have:
'first, second, third'.split(',');
// Array(3) : [ 'first', 'second', 'third' ]
I want to have an object like this
{ 'first': 'first', 'second': 'second', 'third': 'third' }
By doing this right now I get:
{ ...'first, second, third'.split(',') };
// { 1: 'first', 2: 'second', 3: 'third' }
Can I dynamically set it or I have to iterate through and do it manually at this point?
I've ended up combine the two answers to use this:
const toObject = str => Object.assign(...str.split(/\s*,\s*/).map(key => ({ [key]: key })));

You could spread a list of key/value pairs into object assign:
Object.assign(...'first, second, third'.split(',').map(key => ({[key]: key})))

Jonas' solution is clever. I like it. Here's an alternative:
function toObject(str) {
const parts = str.split(/\s*,\s*/);
return parts.reduce((obj, part) => {
obj[part] = part;
return obj;
}, {});
}
console.log(toObject('first, second, third'));
Note that I use split(/\s*,\s*/) instead of split(',') to eliminate whitespace between parts.
This can be reduced to the following one-liner, if you're into that sort of thing:
const toObject = str =>
str.split(/\s*,\s*/).reduce((o, p) => (o[p] = p, o), {});
console.log(toObject('first, second, third'));

Related

Merging the contents of my array into one object

I have an array of several objects
array1 =[ {1:Eat},{2:Shower},{3:Shave}]
I want to convert this array into a single object
object1 ={1:Eat, 2:Shower, 3:Shave}
How do I go about it. The keys(1,2,3) are unique
THE SOLUTION
array.reduce((a, v) => Object.assign(a,v), {})
THE EXPLANATION
Array.reduce() loops throw the initial array aimed to "reduce" it into a single object. The reduce method allows you to store the return value of each of your iterations. It requires an initial value which in this case it will be the empty object {} that we will be filling up with our array.
Then we use Object.assign(target, source) which concatenates two objects together returning the concatenated object which in this case it will saved into our accumulator and be used as a target for the next iteration giving you when it finishes the constructed final object. As you have objects as the keys of the array, each single property Object(source) will be "assigned" to a master one (target) that will be collecting all of them into one.
array1 =[ {1:'Eat'},{2:'Shower'},{3:'Shave'}]
obj1 = array1.reduce((a, v) => Object.assign(a,v), {})
console.log(obj1)
The easiest way to achieve this is to reduce the list of objects into a single object.
const
input = [{ 1: 'Eat' }, { 2: 'Shower' }, { 3: 'Shave' }],
output = input.reduce((acc, obj) => ({ ...acc, ...obj }), {});
console.log(output);
.as-console-wrapper { top: 0; max-height: 100% !important; }
fromEntries and a flatMap seems to do the trick nicely
const array1 = [ {1:'Eat'},{2:'Shower'},{3:'Shave'}]
const obj1 = Object.fromEntries(array1.flatMap(item => Object.entries(item)))
console.log(obj1)
You could simply loop through the array and each object within it and add it to a master object as shown below.
const array1 =[{ 1: 'Eat' }, { 2: 'Shower' }, { 3: 'Shave' }];
const masterObj = {};
array1.forEach(obj => {
for (const property in obj) {
masterObj[property] = obj[property];
};
});
console.log(masterObj);
I'm not sure if my method of using Object.keys() and Object.values() is the most efficient method, but it's something to get you started:
array1 = [{
1: "Eat"
}, {
2: "Shower"
}, {
3: "Shave"
}];
object1 = {};
array1.forEach(v => {
object1[Object.keys(v)[0]] = Object.values(v)[0];
});
console.log(object1);

How to convert an Array to Array of objects with same keys in Javascript? [duplicate]

This question already has answers here:
Javascript string array to object [duplicate]
(4 answers)
JS : Convert Array of Strings to Array of Objects
(1 answer)
Closed 1 year ago.
Consider a simple array that contains a set of values.
var arr = ['ab', 'cd', 'ef'];
I'd like to convert the above array into,
[
{ name: 'ab' },
{ name: 'cd' },
{ name: 'ef' }
];
What I've tried:
arr.reduce((a, c) => ((a[c] = c), a), {});
You can do this using Array.map, which allows you to specify a function that returns a new item to replace in the array.
arr.map(o => ({ name: o }))
Here's one without fancy arrow function shorthand, just in case you are confused.
arr.map(function(o) {
return {
name: o,
}
})
You've tried: arr.reduce((a, c) => ((a[c] = c), a), {});
Your code there are 2 problems:
{}: The initialValue should be the Array instead.
(a[c] = c: You want to define the specific name instead of each c value
The .reduce syntax
arr.reduce(callback( accumulator, currentValue, [, index[, array]] )[,
initialValue])
The finish solution .reduce should be like this:
const arr = ['ab', 'cd', 'ef'];
const result = arr.reduce((acc, currItem) => {
acc.push({name: currItem});
return acc;
}, []);
console.log(result);
Or just .map() like this.
const arr = ['ab', 'cd', 'ef'];
const result = arr.map(item => ({name: item}));
console.log(result);
Another way using With Object.fromEntries, you can convert from Array to Object
const arr = ['ab', 'cd', 'ef'];
const result = arr.map(item => Object.fromEntries([["name",item]]));
console.log(result);
You can use forEach() and create a new array of objects pushing each new object.
var arr = ['ab', 'cd', 'ef'];
const newObjArr = [];
arr.forEach((str) => newObjArr.push({ name: str }))
console.log(newObjArr); //[ { name: 'ab' }, { name: 'cd' }, { name: 'ef' } ]

typescript syntax - how to single line filter/map array [duplicate]

I have an array of objects that I want to iterate over to produce a new filtered array. But also, I need to filter out some of the objects from the new array depending of a parameter. I'm trying this:
function renderOptions(options) {
return options.map(function (option) {
if (!option.assigned) {
return (someNewObject);
}
});
}
Is that a good approach? Is there a better method? I'm open to use any library such as lodash.
You should use Array.reduce for this.
var options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
var reduced = options.reduce(function(filtered, option) {
if (option.assigned) {
var someNewValue = { name: option.name, newProperty: 'Foo' }
filtered.push(someNewValue);
}
return filtered;
}, []);
document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>
Alternatively, the reducer can be a pure function, like this
var reduced = options.reduce(function(result, option) {
if (option.assigned) {
return result.concat({
name: option.name,
newProperty: 'Foo'
});
}
return result;
}, []);
Since 2019, Array.prototype.flatMap is good option.
options.flatMap(o => o.assigned ? [o.name] : []);
From the MDN page linked above:
flatMap can be used as a way to add and remove items (modify the
number of items) during a map. In other words, it allows you to map
many items to many items (by handling each input item separately),
rather than always one-to-one. In this sense, it works like the
opposite of filter. Simply return a 1-element array to keep the item,
a multiple-element array to add items, or a 0-element array to remove
the item.
Use reduce, Luke!
function renderOptions(options) {
return options.reduce(function (res, option) {
if (!option.assigned) {
res.push(someNewObject);
}
return res;
}, []);
}
With ES6 you can do it very short:
options.filter(opt => !opt.assigned).map(opt => someNewObject)
I'd make a comment, but I don't have the required reputation. A small improvement to Maxim Kuzmin's otherwise very good answer to make it more efficient:
const options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const filtered = options
.reduce((result, { name, assigned }) => assigned ? result.push(name) && result : result, []);
console.log(filtered);
Explanation
Instead of spreading the entire result over and over for each iteration, we only append to the array, and only when there's actually a value to insert.
One line reduce with ES6 fancy spread syntax is here!
var options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const filtered = options
.reduce((result, {name, assigned}) => [...result, ...assigned ? [name] : []], []);
console.log(filtered);
At some point, isn't it easier(or just as easy) to use a forEach
var options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
var reduced = []
options.forEach(function(option) {
if (option.assigned) {
var someNewValue = { name: option.name, newProperty: 'Foo' }
reduced.push(someNewValue);
}
});
document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>
However it would be nice if there was a malter() or fap() function that combines the map and filter functions. It would work like a filter, except instead of returning true or false, it would return any object or a null/undefined.
Use Array.prototype.filter:
function renderOptions(options) {
return options.filter(function(option){
return !option.assigned;
}).map(function (option) {
return (someNewObject);
});
}
I optimized the answers with the following points:
Rewriting if (cond) { stmt; } as cond && stmt;
Use ES6 Arrow Functions
I'll present two solutions, one using forEach, the other using reduce:
Solution 1: Using forEach
The solution works by using forEach to iterate through every element. Then, in the body of the forEach loop, we have the conditional to act as a filter and it determines whether we are going to append something to the result array.
const options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const reduced = [ ];
options.forEach(o => {
o.assigned && reduced.push( { name: o.name, newProperty: 'Foo' } );
} );
console.log(reduced);
Solution 2: Using reduce
This solution uses Array.prototype.reduce instead of forEach to iterate through the array. It recognizes the fact that reduce has both an initializer and a looping mechanism built in. Other than that, this solution is more or less the same as the forEach solution, so, the difference comes down to cosmetic syntax sugar.
const options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const reduced = options.reduce((a, o) => {
o.assigned && a.push( { name: o.name, newProperty: 'Foo' } );
return a;
}, [ ] );
console.log(reduced);
I leave it up to you to decide which solution to go for.
Using reduce, you can do this in one Array.prototype function. This will fetch all even numbers from an array.
var arr = [1,2,3,4,5,6,7,8];
var brr = arr.reduce((c, n) => {
if (n % 2 !== 0) {
return c;
}
c.push(n);
return c;
}, []);
document.getElementById('mypre').innerHTML = brr.toString();
<h1>Get all even numbers</h1>
<pre id="mypre"> </pre>
You can use the same method and generalize it for your objects, like this.
var arr = options.reduce(function(c,n){
if(somecondition) {return c;}
c.push(n);
return c;
}, []);
arr will now contain the filtered objects.
I've covert these great answers into utility functions and I'd like to share them:
Example: filter only odd numbers and increment it
e.g. [1, 2, 3, 4, 5] -filter-> [1, 3, 5] -map-> [2, 4, 6]
Normally you'd do it like this with filter and map
const inputArray = [1, 2, 3, 4, 5];
const filterOddPlusOne = inputArray.filter((item) => item % 2).map((item) => item + 1); // [ 2, 4, 6 ]
Using reduce
const filterMap = <TSource, TTarget>(
items: TSource[],
filterFn: (item: TSource) => boolean,
mapFn: (item: TSource) => TTarget
) =>
items.reduce((acc, cur): TTarget[] => {
if (filterFn(cur)) return [...acc, mapFn(cur)];
return acc;
}, [] as TTarget[]);
Using flatMap
const filterMap = <TSource, TTarget>(
items: TSource[],
filterFn: (item: TSource) => boolean,
mapFn: (item: TSource) => TTarget
) => items.flatMap((item) => (filterFn(item) ? [mapFn(item)] : []));
Usage (same for both reduce and flatMap solution):
const inputArray = [1, 2, 3, 4, 5];
const filterOddPlusOne = filterMap(
inputArray,
(item) => item % 2, // Filter only odd numbers
(item) => item + 1 // Increment each number
); // [ 2, 4, 6 ]
JavaScript version
The above codes are in TypeScript but the question asks about JavaScript. So, I've remove all the generics and types for you:
const filterMap = (items, filterFn, mapFn) =>
items.reduce((acc, cur) => {
if (filterFn(cur)) return [...acc, mapFn(cur)];
return acc;
}, []);
const filterMap = (items, filterFn, mapFn) =>
items.flatMap((item) => (filterFn(item) ? [mapFn(item)] : []));
Direct use of .reduce can be hard to read, so I'd recommend creating a function that generates the reducer for you:
function mapfilter(mapper) {
return (acc, val) => {
const mapped = mapper(val);
if (mapped !== false)
acc.push(mapped);
return acc;
};
}
Use it like so:
const words = "Map and filter an array #javascript #arrays";
const tags = words.split(' ')
.reduce(mapfilter(word => word.startsWith('#') && word.slice(1)), []);
console.log(tags); // ['javascript', 'arrays'];
You can use Array.reduce with an arrow function is a single line of code
const options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const reduced = options.reduce((result, option) => option.assigned ? result.concat({ name: option.name, newProperty: 'Foo' }) : result, []);
document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>
The most efficient way of doing filter + map at once is to process data as a generic iterable, and do both things at once. In this case, you will end up going through data once, at most.
The example below is using iter-ops library, and doing exactly that:
import {pipe, filter, map} from 'iter-ops';
const i = pipe(
inputArray,
filter(value => value === 123), // filter on whatever key you want
map(value => /* any mapping here*/) // remap data as you like
);
// i = iterable that can be processed further;
console.log([...i]); //=> list of new objects
Above, I was saying at most, because if you apply further logic to the iterable result, like limit the number of mapped items, for example, you will end up iterating through your list of objects even less than once:
const i = pipe(
inputArray,
filter(value => value === 123), // filter on whatever key you want
map(value => /* any mapping here*/), // remap as you like
take(10) // take up to 10 items only
);
Above, we limit iteration further, to stop once 10 resulting items have been generated, and so we are iterating through data less than once. That's as efficient as it gets.
UPDATE
I was asked to add to the answer why this solution is more efficient than reduce, and so here it is...
Array's reduce is a finite operation, which goes through the complete set of data, in order to produce the result. So when you need to do further processing on that output data, you will end up producing a new iteration sequence, and so on.
When you have a complex business logic to be applied to a sequence/iterable, it is always much more efficient to chain that logic, while iterating through the sequence just once. In many cases, you will end up doing complex processing on a sequence, without going through the complete set of data even once. That's the efficiency of iterable data processing.
P.S. I'm the author of the aforesaid library.
Hey I've just worked on this project and wanted to share my solution based on Array.prototype.flatMap() in MDN docs:
const places=[{latitude:40,longitude:1},{latitude:41,longitude:2},{latitude:44,longitude:2},{latitude:NaN,longitude:NaN},{latitude:45,longitude:4},{latitude:48,longitude:3},{latitude:44,longitude:5},{latitude:39,longitude:13},{latitude:40,longitude:8},{latitude:38,longitude:4}];
let items = places?.map((place) => [{
latitude: (place.latitude),
longitude: (place.longitude),
}, ]);
console.log("Items: ", items);
//Remove elements with NaN latitude and longitude
let newItems = places?.flatMap((o) =>
Number(o.longitude, o.latitude) ?
{
lng: Number(o.longitude),
lat: Number(o.latitude)
} :
[]
);
console.log("Coordinates after NaN values removed: ", newItems);
Same approach as the top answers, using Array.prototype.reduce(), but with updated ES6 syntax, and TypeScript typings, as a generic utility function:
function filterThenMap<T>(l: T[], predicate: (el: T) => boolean, transform: (el: T) => T) {
return l.reduce((res: T[], el) => {
if (predicate(el)) {
res.push(transform(el));
}
return res;
}, []);
}

Observable transformation

I'm very new to observables, this might be a silly question for others but I can't find a way to do it. How do I convert
Observable<Array<T>> where T is a TypeScript class with property Id(string) into an Array<string>? I want to convert the Id properties into a string array.
You are trying to do 2 things here:
Flatten all arrays from the source array into a single observable of T
Map each instance of T to Id property
You can use mergeAll to flatten the observable as you would use reduce on a normal JS array.
const source = Rx.Observable.of(
[{ Id: '1' }, { Id: '2' }],
[{ Id: '3' }, { Id: '4' }]
);
const flattened = source.mergeAll();
flattened.subscribe(s => console.log(s));
// => {Id:'1'}, {Id:'2'}, {Id:'3'}, {Id:'4'}
You can then use map (as with a normal JS array) to extract the Id property
const mapped = flattened.map(s => s.Id);
mapped.subscribe(s => console.log(s))
// => '1', '2', '3', '4'
Pull it all into 1 statement and...
source
.mergeAll()
.map(s => s.Id)
.subscribe(s => console.log(s))
// => '1', '2', '3', '4'
You can use reduce operator that like on javascript array. RxJS reduce operator will operate on the whole stream when it is completed.
See the example:
let data$ = new Rx.Subject();
data$
.reduce((acc, el) => {acc.push(...el); return acc;}, [])
.subscribe(x=>console.log(x));
data$.next([{id:'1'}, {id:'2'}]);
data$.next([{id:'3'}, {id:'4'}]);
data$.complete();

Map and filter an array at the same time

I have an array of objects that I want to iterate over to produce a new filtered array. But also, I need to filter out some of the objects from the new array depending of a parameter. I'm trying this:
function renderOptions(options) {
return options.map(function (option) {
if (!option.assigned) {
return (someNewObject);
}
});
}
Is that a good approach? Is there a better method? I'm open to use any library such as lodash.
You should use Array.reduce for this.
var options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
var reduced = options.reduce(function(filtered, option) {
if (option.assigned) {
var someNewValue = { name: option.name, newProperty: 'Foo' }
filtered.push(someNewValue);
}
return filtered;
}, []);
document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>
Alternatively, the reducer can be a pure function, like this
var reduced = options.reduce(function(result, option) {
if (option.assigned) {
return result.concat({
name: option.name,
newProperty: 'Foo'
});
}
return result;
}, []);
Since 2019, Array.prototype.flatMap is good option.
options.flatMap(o => o.assigned ? [o.name] : []);
From the MDN page linked above:
flatMap can be used as a way to add and remove items (modify the
number of items) during a map. In other words, it allows you to map
many items to many items (by handling each input item separately),
rather than always one-to-one. In this sense, it works like the
opposite of filter. Simply return a 1-element array to keep the item,
a multiple-element array to add items, or a 0-element array to remove
the item.
Use reduce, Luke!
function renderOptions(options) {
return options.reduce(function (res, option) {
if (!option.assigned) {
res.push(someNewObject);
}
return res;
}, []);
}
With ES6 you can do it very short:
options.filter(opt => !opt.assigned).map(opt => someNewObject)
I'd make a comment, but I don't have the required reputation. A small improvement to Maxim Kuzmin's otherwise very good answer to make it more efficient:
const options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const filtered = options
.reduce((result, { name, assigned }) => assigned ? result.push(name) && result : result, []);
console.log(filtered);
Explanation
Instead of spreading the entire result over and over for each iteration, we only append to the array, and only when there's actually a value to insert.
One line reduce with ES6 fancy spread syntax is here!
var options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const filtered = options
.reduce((result, {name, assigned}) => [...result, ...assigned ? [name] : []], []);
console.log(filtered);
At some point, isn't it easier(or just as easy) to use a forEach
var options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
var reduced = []
options.forEach(function(option) {
if (option.assigned) {
var someNewValue = { name: option.name, newProperty: 'Foo' }
reduced.push(someNewValue);
}
});
document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>
However it would be nice if there was a malter() or fap() function that combines the map and filter functions. It would work like a filter, except instead of returning true or false, it would return any object or a null/undefined.
Use Array.prototype.filter:
function renderOptions(options) {
return options.filter(function(option){
return !option.assigned;
}).map(function (option) {
return (someNewObject);
});
}
I optimized the answers with the following points:
Rewriting if (cond) { stmt; } as cond && stmt;
Use ES6 Arrow Functions
I'll present two solutions, one using forEach, the other using reduce:
Solution 1: Using forEach
The solution works by using forEach to iterate through every element. Then, in the body of the forEach loop, we have the conditional to act as a filter and it determines whether we are going to append something to the result array.
const options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const reduced = [ ];
options.forEach(o => {
o.assigned && reduced.push( { name: o.name, newProperty: 'Foo' } );
} );
console.log(reduced);
Solution 2: Using reduce
This solution uses Array.prototype.reduce instead of forEach to iterate through the array. It recognizes the fact that reduce has both an initializer and a looping mechanism built in. Other than that, this solution is more or less the same as the forEach solution, so, the difference comes down to cosmetic syntax sugar.
const options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const reduced = options.reduce((a, o) => {
o.assigned && a.push( { name: o.name, newProperty: 'Foo' } );
return a;
}, [ ] );
console.log(reduced);
I leave it up to you to decide which solution to go for.
Using reduce, you can do this in one Array.prototype function. This will fetch all even numbers from an array.
var arr = [1,2,3,4,5,6,7,8];
var brr = arr.reduce((c, n) => {
if (n % 2 !== 0) {
return c;
}
c.push(n);
return c;
}, []);
document.getElementById('mypre').innerHTML = brr.toString();
<h1>Get all even numbers</h1>
<pre id="mypre"> </pre>
You can use the same method and generalize it for your objects, like this.
var arr = options.reduce(function(c,n){
if(somecondition) {return c;}
c.push(n);
return c;
}, []);
arr will now contain the filtered objects.
I've covert these great answers into utility functions and I'd like to share them:
Example: filter only odd numbers and increment it
e.g. [1, 2, 3, 4, 5] -filter-> [1, 3, 5] -map-> [2, 4, 6]
Normally you'd do it like this with filter and map
const inputArray = [1, 2, 3, 4, 5];
const filterOddPlusOne = inputArray.filter((item) => item % 2).map((item) => item + 1); // [ 2, 4, 6 ]
Using reduce
const filterMap = <TSource, TTarget>(
items: TSource[],
filterFn: (item: TSource) => boolean,
mapFn: (item: TSource) => TTarget
) =>
items.reduce((acc, cur): TTarget[] => {
if (filterFn(cur)) return [...acc, mapFn(cur)];
return acc;
}, [] as TTarget[]);
Using flatMap
const filterMap = <TSource, TTarget>(
items: TSource[],
filterFn: (item: TSource) => boolean,
mapFn: (item: TSource) => TTarget
) => items.flatMap((item) => (filterFn(item) ? [mapFn(item)] : []));
Usage (same for both reduce and flatMap solution):
const inputArray = [1, 2, 3, 4, 5];
const filterOddPlusOne = filterMap(
inputArray,
(item) => item % 2, // Filter only odd numbers
(item) => item + 1 // Increment each number
); // [ 2, 4, 6 ]
JavaScript version
The above codes are in TypeScript but the question asks about JavaScript. So, I've remove all the generics and types for you:
const filterMap = (items, filterFn, mapFn) =>
items.reduce((acc, cur) => {
if (filterFn(cur)) return [...acc, mapFn(cur)];
return acc;
}, []);
const filterMap = (items, filterFn, mapFn) =>
items.flatMap((item) => (filterFn(item) ? [mapFn(item)] : []));
Direct use of .reduce can be hard to read, so I'd recommend creating a function that generates the reducer for you:
function mapfilter(mapper) {
return (acc, val) => {
const mapped = mapper(val);
if (mapped !== false)
acc.push(mapped);
return acc;
};
}
Use it like so:
const words = "Map and filter an array #javascript #arrays";
const tags = words.split(' ')
.reduce(mapfilter(word => word.startsWith('#') && word.slice(1)), []);
console.log(tags); // ['javascript', 'arrays'];
You can use Array.reduce with an arrow function is a single line of code
const options = [
{ name: 'One', assigned: true },
{ name: 'Two', assigned: false },
{ name: 'Three', assigned: true },
];
const reduced = options.reduce((result, option) => option.assigned ? result.concat({ name: option.name, newProperty: 'Foo' }) : result, []);
document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>
The most efficient way of doing filter + map at once is to process data as a generic iterable, and do both things at once. In this case, you will end up going through data once, at most.
The example below is using iter-ops library, and doing exactly that:
import {pipe, filter, map} from 'iter-ops';
const i = pipe(
inputArray,
filter(value => value === 123), // filter on whatever key you want
map(value => /* any mapping here*/) // remap data as you like
);
// i = iterable that can be processed further;
console.log([...i]); //=> list of new objects
Above, I was saying at most, because if you apply further logic to the iterable result, like limit the number of mapped items, for example, you will end up iterating through your list of objects even less than once:
const i = pipe(
inputArray,
filter(value => value === 123), // filter on whatever key you want
map(value => /* any mapping here*/), // remap as you like
take(10) // take up to 10 items only
);
Above, we limit iteration further, to stop once 10 resulting items have been generated, and so we are iterating through data less than once. That's as efficient as it gets.
UPDATE
I was asked to add to the answer why this solution is more efficient than reduce, and so here it is...
Array's reduce is a finite operation, which goes through the complete set of data, in order to produce the result. So when you need to do further processing on that output data, you will end up producing a new iteration sequence, and so on.
When you have a complex business logic to be applied to a sequence/iterable, it is always much more efficient to chain that logic, while iterating through the sequence just once. In many cases, you will end up doing complex processing on a sequence, without going through the complete set of data even once. That's the efficiency of iterable data processing.
P.S. I'm the author of the aforesaid library.
Hey I've just worked on this project and wanted to share my solution based on Array.prototype.flatMap() in MDN docs:
const places=[{latitude:40,longitude:1},{latitude:41,longitude:2},{latitude:44,longitude:2},{latitude:NaN,longitude:NaN},{latitude:45,longitude:4},{latitude:48,longitude:3},{latitude:44,longitude:5},{latitude:39,longitude:13},{latitude:40,longitude:8},{latitude:38,longitude:4}];
let items = places?.map((place) => [{
latitude: (place.latitude),
longitude: (place.longitude),
}, ]);
console.log("Items: ", items);
//Remove elements with NaN latitude and longitude
let newItems = places?.flatMap((o) =>
Number(o.longitude, o.latitude) ?
{
lng: Number(o.longitude),
lat: Number(o.latitude)
} :
[]
);
console.log("Coordinates after NaN values removed: ", newItems);
Same approach as the top answers, using Array.prototype.reduce(), but with updated ES6 syntax, and TypeScript typings, as a generic utility function:
function filterThenMap<T>(l: T[], predicate: (el: T) => boolean, transform: (el: T) => T) {
return l.reduce((res: T[], el) => {
if (predicate(el)) {
res.push(transform(el));
}
return res;
}, []);
}

Categories