with
let str = "baaabbabbsbsssssabbaaaa";
with this
[...str].reduce((characterMap, char) => {
if (!characterMap[char]) characterMap[char] = 1;
else characterMap[char]++;
return characterMap;
}, {})
I get { b: 8, a: 9, s: 6 }
But when I shorten it to this,
[...str].reduce((characterMap, char) => !characterMap[char] ? characterMap[char] = 1 : characterMap[char]++, {});
It prints out 1 instead of { b: 8, a: 9, s: 6 }
Why?
am I using reduce wrong in the first place?
how do I shorten this?
By:
Not trying to shoehorn it in to a reduce
. :-) reduce
is appropriately used when the accumulator value changes . In your case, it doesn't.
Not using an array wrapper. Strings are iterable, so use that fact directly.
Using the curiously-powerful ||
operator ¹ to default the character count if not present.
Just use a loop:
const characterMap = {};
for (const char of str) {
characterMap[char] = (characterMap[char] || 0) + 1;
}
Live Example:
let str = "baaabbabbsbsssssabbaaaa"; const characterMap = {}; for (const char of str) { characterMap[char] = (characterMap[char] || 0) + 1; } console.log(characterMap);
But if you really want to use reduce
, you can apply the ||
trick and use a shorter name:
const characterMap = [...str].reduce((acc, ch) => {
acc[ch] = (acc[ch] || 0) + 1;
return acc;
}, {});
Live Example:
let str = "baaabbabbsbsssssabbaaaa"; const characterMap = [...str].reduce((acc, ch) => { acc[ch] = (acc[ch] || 0) + 1; return acc; }, {}); console.log(characterMap);
You can (ab)use the comma operator to force that into a concise arrow function, but at the cost of readability and maintainability (IMHO).
But when I shorten it to this,
[...str].reduce((characterMap, char) => !characterMap[char] ? characterMap[char] = 1 : characterMap[char]++, {});
It prints out 1 instead of { b: 8, a: 9, s: 6 }
Why?
Because your callback function returns the result of !characterMap[char] ? characterMap[char] = 1 : characterMap[char]++
!characterMap[char] ? characterMap[char] = 1 : characterMap[char]++
, not the object. So subsequent calls to it get that number, not the object, and characterMap[char]
is always false (because 1["a"]
is undefined
, etc.).
¹ (on my anemic little blog)
You need to take the returned accumulator as result.
var str = "baaabbabbsbsssssabbaaaa", result = [...str].reduce((characterMap, char) => { characterMap[char] = (characterMap[char] || 0) + 1; // take default value of zero return characterMap; }, {}); console.log(result)
But when I shorten it to this,
[...str].reduce((characterMap, char) => !characterMap[char] ? characterMap[char] = 1 : characterMap[char]++, {});
It prints out 1 instead of
{ b: 8, a: 9, s: 6 }
Why?
Simply because in the first loop
!characterMap[char] ? characterMap[char] = 1 : characterMap[char]++
it takes this route
!characterMap[char] ? characterMap[char] = 1 : characterMap[char]++
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
true characterMap['b'] = 1
returns 1
and in all following iterations, you take the number 1
and try to get a property a
(or all following letters of the string) of this number, which is undefined
!characterMap[char] ? characterMap[char] = 1 : characterMap[char]++
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
!1['a']
true 1['b'] = 1
The result is 1
If you want to stick with the arrow-expression syntax, you can use the comma operator :
[...str].reduce((characterMap, chr) =>
(characterMap[chr] = (characterMap[chr] || 0) + 1, characterMap), {});
Or you can use Object.assign
:
[...str].reduce((characterMap, chr) =>
Object.assign(characterMap, {[chr]: (characterMap[chr] || 0) + 1}), {});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.