I am trying to nest a recursive function in an Array.reduce call.
But the following does not work for the third instance only
const i1 = {'local': [['a','b']]};
const i2 = {'local': [['c','d']], 'recursive': []};
const i3 = {'local': [['c','d']], 'recursive': [{'local': [['e','f']]}]};
function reduce(current, result = []) {
if(current.hasOwnProperty('local'))
result.push(...current.local);
if(current.hasOwnProperty('recursive'))
result = current.recursive.reduce(reduce, result);
//'result =' should be optional, but yields a wrong answer anyway
return result;
}
console.log(reduce(i1));
console.log(reduce(i2));
console.log(reduce(i3));
So instead of calling reduce I tried the following loop
for(var i = 0; i < current.recursive.length; ++i)
result = reduce(current.recursive[i], result);
//'result = ' is optional for it is passed by reference
and it works. Being new to JavaScript, I am certain of missing a key feature here, so could you explain ?
Output for the third instance should be
[ [ 'c', 'd' ], [ 'e', 'f' ] ]
but is
{ local: [ [ 'e', 'f' ] ] }
or
[ [ 'c', 'd' ] ]
when result = is removed.
3 Answers 3
The problem is the order of the parameters, the first argument to the reduce callback function is the accumulator not the current value. Try this:
const i1 = {'local': [['a','b']]};
const i2 = {'local': [['c','d']], 'recursive': []};
const i3 = {'local': [['c','d']], 'recursive': [{'local': [['e','f']]}]};
function reduce(result, current) {
if(current.hasOwnProperty('local'))
result.push(...current.local);
if(current.hasOwnProperty('recursive'))
result = current.recursive.reduce(reduce, result);
//'result =' should be optional, but yields a wrong answer anyway
return result;
}
console.log(reduce([], i1));
console.log(reduce([], i2));
console.log(reduce([], i3));
2 Comments
reduce(i1). Add if (current === undefined) { current = result; result = []; } to the start of the reduce function.I think what you want to implement is flatten - It's no coincidence that it's a simple wrapper for Array.prototype.flatMap -
const i1 =
{ local: [[1,2]] }
const i2 =
{ local: [[3,4]], recursive: [] }
const i3 =
{ local: [[3,4]], recursive: [{ local: [[5,6]] }] }
const i4 =
{ local: [[1,2]],
recursive: [
{ local: [[3,4]] },
{ local: [[5,6]] , recursive: [{ local: [[7,8]] }] }] }
const flatten = ({ local = [], recursive = [] }) =>
[ ...local, ...recursive.flatMap(flatten) ]
console.log(flatten(i1))
// [ [ 1, 2 ] ]
console.log(flatten(i2))
// [ [ 3, 4 ] ]
console.log(flatten(i3))
// [ [ 3, 4 ], [ 5, 6 ] ]
console.log(flatten(i4))
// [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ]
The spread arguments can be traded for Array.prototype.concat, if that is preferred -
const flatten = ({ local = [], recursive = [] }) =>
(削除) [ ...local, ...recursive.flatMap(flatten) ] (削除ここまで)
local.concat(recursive.flatMap(flatten))
Array.prototype.flatMap is a special kind of Array.prototype.reduce -
const flatten = ({ local = [], recursive = [] }) =>
local.concat(
recursive.reduce((r, x) => r.concat(flatten(x)), [])
)
And since Array.prototype.concat is a pure operation, we can simplify it a bit more -
const flatten = ({ local = [], recursive = [] }) =>
recursive.reduce((r, x) => r.concat(flatten(x)), local)
And finally we see it again using reduce and array spread arguments -
const flatten = ({ local = [], recursive = [] }) =>
recursive.reduce((r, x) => [...r, ...flatten(x)], local)
Each of these flatten produce the exact same outputs and the input arrays are not mutated in the process. Hopefully this gives you a little insight on how the helpful Array.prototype.flatMap works.
Comments
This only covers the cases when local and recursive only includes one element, like in your example.
const i1 = { local: [["a", "b"]] };
const i2 = { local: [["c", "d"]], recursive: [] };
const i3 = { local: [["c", "d"]], recursive: [{ local: [["e", "f"]] }] };
const i4 = {
local: [["c", "d"]],
recursive: [{ local: [["e", "f"]], recursive: [{ local: [["g", "h"]] }] }]
};
const solution = ({ local: [firstItem], recursive }) =>
[firstItem].concat(
recursive && recursive.length ? solution(recursive[0]) : []
);
console.log(solution(i1));
console.log(solution(i2));
console.log(solution(i3));
console.log(solution(i4));
1 Comment
local and recursive are arrays so it makes sense to me to append all children, not just the first, firstItem and recursive[0]
Array#reduce? why do you need a recursion? the data base is not the same and the signature ofArray#reduceneeds the second parameter for the element.Array#reduce, mixing assignments and functional style, and naming your own functionreduce. But I think the main issue is that your order of arguments is wrong. It should befunction reduce(result, current).reducetakes the accumulator first and the element second.