问题
This function is written in JavaScript but I think that the concept can be implemented with some other programming languages.
function uniteUnique(arr) {
let seenBefore = []; //the accumulating array
for (let item of arguments) {
if (typeof (item) == "object") {
uniteUnique(...item);
}
else if (!seenBefore.includes(item)) {
seenBefore.push(item);
}
}
return seenBefore;
}
In short, the function iterates over arrays it receives as arguments, which may or may not contain other arrays themselves. the deepest level of any of those arrays contains int
values. The function returns an array that contains all those int
s (i.e those that appeared in nested arrays), but it returns each int only once, even if it appeared more than one time.
My problem lies in the fact that every time the recursion returns to a higher level, it initializes again the array that contains the saved int
s, namely the array that the function needs to return (seenBefore
), and therefore ruins the whole process. On one hand, I must initialize the array when the function starts, but on the other hand, it is initialized more than once and loses its previously stored values.
for example, if I would run the function
uniteUnique([1, 3, [6, 3], 2], [5, 2, 1, 4], [2, 1]);
the output should be
[1,3,6,2,5,4]
because the function has to return the numbers it stumble upon only once by the order of processing. The function actually returns an empty array because it is initialized again right before the function returns from the top-most level of the recursion.
How can I bypass this problem?
(P.S: I know this can be "solved" by pulling the accumulating array out of the function to a different scope, but that causes other problems, such as the need to reinitialize the accumulating array each time before I run the function if I run it more than once.)
回答1:
You've misidentified your problem. Each call to uniteUnique()
has a separate value for the local variable seenBefore
-- nothing is being "initialized again" during recursive calls.
Your real problem is that the line:
uniteUnique(...item);
discards the result of that function call, so the contents of any nested arrays are ignored. You need to assign the return value of this function somewhere and use it.
You may also want to change the condition for this function call to:
if (Array.isArray(item)) {
as the current condition typeof item == "object"
will include objects which cannot be iterated over.
回答2:
Another possibility would be to define seenBefore
as the empty array as a default second parameter, which is recursively passed to every call of the function:
function uniteUnique(arr, seenBefore = []) {
for (const item of arr) {
if (typeof (item) == "object") {
uniteUnique(item, seenBefore);
}
else if (!seenBefore.includes(item)) {
seenBefore.push(item);
}
}
return seenBefore;
}
uniteUnique(someArr);
Note that this accepts a single argument as an array, rather than multiple arguments.
回答3:
You can utilize a nested function, so that you don't need to reinitialize the accumulating array each time:
function uniteUnique(arr) {
function iterate(seenBefore, arr)
{
for (let item of arr) {
if (Array.isArray(item)) {
iterate(seenBefore, item);
}
else if (!seenBefore.includes(item)) {
seenBefore.push(item);
}
}
}
let result = []; // the accumulating array
iterate(result, arr);
return result ;
}
You don't actually need to use arguments
and spread operator here, because your function expects an array and you can simply pass an array.
You may also want to use Set
for seenBefore
, because Array.prototype.includes
scans through an array, which is ineffective.
回答4:
Just in case: if you can use the last ES2019 features and prefer simplicity, this can also be achieved with a one-liner:
const uniquesArray = [...new Set(nestedArray.flat(Infinity))];
来源:https://stackoverflow.com/questions/54412311/how-to-prevent-a-recursive-function-from-re-initializing-an-accumulating-variabl