Reputation: 447
I have been struggeling with a "simple" task for some time now and have figured out how to solve the problem in an alternative way. But I still would like to know what I was doing wrong in my first attempt where I used reduce
.
My goal is to count the number of blogs for each author so that I get this result: { 'Michael Chan': 1, 'Edsger W. Dijkstra': 2, 'Robert C. Martin': 3 }
My first attempt:
blogs.reduce((acc,curr)=> acc[curr.author] ? acc[curr.author]+=1:acc[curr.author]=1,{})
This always returns 1
If I try what I understand is the same thing but with more code:
let temp = {}
blogs.forEach(blog => {
if(temp[blog.author]){
temp[blog.author]+=1
}else{
temp[blog.author] =1
}
This works and gives me: { 'Michael Chan': 1, 'Edsger W. Dijkstra': 2, 'Robert C. Martin': 3 }
The blogarray that I am using:
const blogs = [
{
_id: "5a422a851b54a676234d17f7",
title: "React patterns",
author: "Michael Chan",
url: "https://reactpatterns.com/",
likes: 7,
__v: 0
},
{
_id: "5a422aa71b54a676234d17f8",
title: "Go To Statement Considered Harmful",
author: "Edsger W. Dijkstra",
url: "http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html",
likes: 5,
__v: 0
},
{
_id: "5a422b3a1b54a676234d17f9",
title: "Canonical string reduction",
author: "Edsger W. Dijkstra",
url: "http://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD808.html",
likes: 12,
__v: 0
},
{
_id: "5a422b891b54a676234d17fa",
title: "First class tests",
author: "Robert C. Martin",
url: "http://blog.cleancoder.com/uncle-bob/2017/05/05/TestDefinitions.htmll",
likes: 10,
__v: 0
},
{
_id: "5a422ba71b54a676234d17fb",
title: "TDD harms architecture",
author: "Robert C. Martin",
url: "http://blog.cleancoder.com/uncle-bob/2017/03/03/TDD-Harms-Architecture.html",
likes: 0,
__v: 0
},
{
_id: "5a422bc61b54a676234d17fc",
title: "Type wars",
author: "Robert C. Martin",
url: "http://blog.cleancoder.com/uncle-bob/2016/05/01/TypeWars.html",
likes: 2,
__v: 0
}
]
Upvotes: 0
Views: 52
Reputation: 63587
It's because you're not combining the value of the current property with the accumulator, and returning that result. This maybe simpler:
const blogs=[{_id:"5a422a851b54a676234d17f7",title:"React patterns",author:"Michael Chan",url:"https://reactpatterns.com/",likes:7,__v:0},{_id:"5a422aa71b54a676234d17f8",title:"Go To Statement Considered Harmful",author:"Edsger W. Dijkstra",url:"http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html",likes:5,__v:0},{_id:"5a422b3a1b54a676234d17f9",title:"Canonical string reduction",author:"Edsger W. Dijkstra",url:"http://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD808.html",likes:12,__v:0},{_id:"5a422b891b54a676234d17fa",title:"First class tests",author:"Robert C. Martin",url:"http://blog.cleancoder.com/uncle-bob/2017/05/05/TestDefinitions.htmll",likes:10,__v:0},{_id:"5a422ba71b54a676234d17fb",title:"TDD harms architecture",author:"Robert C. Martin",url:"http://blog.cleancoder.com/uncle-bob/2017/03/03/TDD-Harms-Architecture.html",likes:0,__v:0},{_id:"5a422bc61b54a676234d17fc",title:"Type wars",author:"Robert C. Martin",url:"http://blog.cleancoder.com/uncle-bob/2016/05/01/TypeWars.html",likes:2,__v:0}];
const result = blogs.reduce((acc, curr) => {
acc[curr.author] ? acc[curr.author] += 1 : acc[curr.author] = 1;
return acc;
}, {});
console.log(result);
Upvotes: 1
Reputation: 352
Ternary operator cannot be used with complete statements. The below would have been correct implementation:
blogs.reduce((acc,curr)=> {
acc[curr.author] = acc[curr.author] ? acc[curr.author]+1 : 1;
return acc;
},{});
Upvotes: 1
Reputation: 351403
The value that the reduce
callback returns, should be the accumulator, yet your callback returns the value of an assignment, which is the assigned value, not the object whose property is getting that value.
So if you really want to stay with a single-expression-callback, use the comma operator to still return the accumulator:
blogs.reduce((acc,curr)=>
(acc[curr.author] ? acc[curr.author]+=1:acc[curr.author]=1, acc),
{})
Upvotes: 3
Reputation: 371233
With reduce
, the value of the accumulator in the next iteration will be the value returned from the callback in the prior iteration. So, your original code of:
blogs.reduce((acc,curr)=> acc[curr.author] ? acc[curr.author]+=1:acc[curr.author]=1,{})
is carrying out a process equivalent to:
let result = {};
for (const curr of blogs) {
result = acc[curr.author]
? acc[curr.author] += 1
: acc[curr.author] = 1;
}
But assignments evaluate to the value of what was assigned. For example
let val1;
const someVal = (val1 = 15);
will result in someVal
being 15, since 15 was the value assigned to val1
.
You'll need to return the acc
separately for reduce
to work.
blogs.reduce((acc,curr)=> {
acc[curr.author] ? acc[curr.author]+=1:acc[curr.author]=1;
return acc;
} ,{})
Or, I'd suggest not using reduce
here at all, it doesn't really help over a plain for
loop.
const result = {};
for (const blog of blogs) {
result[blog.author] = (result[blog.author] || 0) + 1;
}
Upvotes: 4