CodeNewbie Community 🌱

Discussion on: Another way to understand JavaScript's array.reduce

Collapse
 
r002 profile image
Robert Lin • Edited

Hi Ben! Thanks so much for sharing this article on reduce(...). I've been recently trying to embrace a more "functional" style of program and write code where I avoid mutating any variables (killing off "standalone variables" as you call them). Reduce is certainly one helpful tool in the toolkit!

Out of curiosity, I'm wondering about your thoughts about putting a reduce(...) inside another reduce(...). Recently, I've been generating a lot of HTML code from JS (I'm trying to avoid React, at least for now, just for learning purposes) and I find myself using reduce all the time to collapse arrays of objects into a single HTML string to render. So for example, to generate an "Article List" GUI widget (screenshot) I've written: .../ts/widgets.ts#L8-L33.

At work, or elsewhere, do you see this kind of usage of reduce often? Is it a bad "code smell"? Any thoughts or opinions?

Thank you for writing this article up! 🙏 Map/Filter/Reduce is the way! ✊

Collapse
 
bholmesdev profile image
Ben Holmes • Edited

Hey thanks Robert! That's a totally fair question. I've def seen nested reduce loops before, but it can be difficult to read if you throw everything together without helper functions.

So this is a little sketchy:

const songsByAlbum = [
  ['Rap Snitches Knishes', 'Beef Rap', 'Gumbo'],
  ['Accordion', 'Meat Grinder', 'Figaro'],
  ['Fazers', 'Anti-Matter', 'Krazy World']
]
songsByAlbum.reduce((songsAsString, album) => {
  // Flatten our album strings to a big string of songs across *all* albums
  return songsAsString + album.reduce((albumAsString, song) => {
    // Flatten album of songs to a string of songs
    return albumAsString + " " + song
  }, "")
}, "")
Enter fullscreen mode Exit fullscreen mode

...but this is a little better!

const flattenToSongs = (songs) => {
  return songs.reduce((songsAsString, song) => {
    return songsAsString + song
  }, "")
}

songsByAlbum.reduce((songsAsString, album) => {
  return songsAsString + flattenToSongs(album)
}, "")
Enter fullscreen mode Exit fullscreen mode

That said, you sometimes don't even need reduce for "stringifying" lists. We could totally do something like this instead:

songsByAlbum.map(album => {
  return album.join(" ") // join our list into a string separated by spaces
}).join(" ") // join this list of "album strings" to a big string
Enter fullscreen mode Exit fullscreen mode

PS: if you're using a nested reduce because you have arrays within arrays, there's a new helper in JS to "flatten" to a single array! It's called array.flat. More on that here

Collapse
 
r002 profile image
Robert Lin • Edited

Very cool, thanks for sharing, Ben! I never knew about array.flat until today. Very neat. 👍

Everyday, I seem to find a new use for array.reduce though. It's seriously the gift that just keeps giving! 😄 Just this morning, I was searching for an equivalent to Python's counter data structure in JS and found this:

var arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]

const map = arr.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map())

console.info([...map.keys()])
console.info([...map.values()])
console.info([...map.entries()])
Enter fullscreen mode Exit fullscreen mode

What a beauty! What a time to be alive!! 🚀

Source: stackoverflow.com/a/57028486

Thread Thread
 
bholmesdev profile image
Ben Holmes

Haha yeah, love the enthusiasm 😁

Agreed, reduce is for a lot more than just reduce-ing arrays to something smaller (like a number of a string). That's why some languages like Rust and Kotlin call it "fold." That's because you're "folding" something of one data type (like an array) into something of another data type (like a Map in your example). Doesn't mean we've reduced it, we've just changed it into something different!