简体   繁体   中英

How to prevent a recursive function from re-initializing an accumulating variable?

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 (ie 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?

(PS: 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.)

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.

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.

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.

以防万一:如果您可以使用最新的ES2019功能并且更喜欢简单,这也可以通过单线程实现:

const uniquesArray = [...new Set(nestedArray.flat(Infinity))];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM