I keep running into situations where I end up nesting very many reduce functions to drill down into an object. It's hard to pull out the logic because at the bottom I need access to the various keys traversed along the way. Essentially, I'm looking for a better way to achieve the following:
import { curry } from 'lodash/fp'
import { fromJS } from 'immutable'
const reduce = curry((fn, acc, it) => it.reduce(fn, acc))
describe('reduceNested', () => {
const input = fromJS({
a1: {
b1: {
c1: {
d1: {
e1: 'one',
e2: 'two',
e3: 'three'
},
d2: {
e1: 'one',
e2: 'two',
e3: 'three'
}
},
c2: {
d1: {
e1: 'one',
e2: 'two'
}
}
}
},
a2: {
b1: {
c1: {
d1: {
e1: 'one'
},
d2: {
e1: 'one'
}
}
},
b2: {
c1: {
d1: {
e1: 'one'
},
d2: {
e1: 'one'
}
}
}
},
a3: {
b1: {
c1: {}
}
}
})
const expected = fromJS({
one: [
'a1.b1.c1.d1.e1',
'a1.b1.c1.d2.e1',
'a1.b1.c2.d1.e1',
'a2.b1.c1.d1.e1',
'a2.b1.c1.d2.e1',
'a2.b2.c1.d1.e1',
'a2.b2.c1.d2.e1'
],
two: ['a1.b1.c1.d1.e2', 'a1.b1.c1.d2.e2', 'a1.b1.c2.d1.e2'],
three: ['a1.b1.c1.d1.e3', 'a1.b1.c1.d2.e3']
})
const init = fromJS({ one: [], two: [], three: [] })
test('madness', () => {
const result = reduce(
(acc2, val, key) =>
reduce(
(acc3, val2, key2) =>
reduce(
(acc4, val3, key3) =>
reduce(
(acc5, val4, key4) =>
reduce(
(acc6, val5, key5) =>
acc6.update(val5, i =>
i.push(`${key}.${key2}.${key3}.${key4}.${key5}`)
),
acc5,
val4
),
acc4,
val3
),
acc3,
val2
),
acc2,
val
),
init,
input
)
expect(result).toEqual(expected)
})
test('better', () => {
const result = reduceNested(
(acc, curr, a, b, c, d, e) =>
acc.update(curr, i => i.push(`${a}.${b}.${c}.${d}.${e}`)),
init,
input
)
expect(result).toEqual(expected)
})
})
I would like to write a function reduceNested that achieves the same result but without all of the nested reduce functions. I don't see something in lodash/fp or similar to address so my thought was to create a new function reduceNested and to add variables to the callback for each key in the tree. I've tried implementing the actual logic but am unfortunately stuck for the time being. I know reduceNested will need to use fn.length to determine how far down into the source to drill, but other than that I'm just stuck.
const reduceNested = curry((fn, acc, iter) => {
// TODO --> use (fn.length - 2)
})
reduceyou want some form of recursion.