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 } ]
Advertisement
Answer
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; }