简体   繁体   中英

Is it safe to join two arrays with for loop and findIndex in Javascript

I have the following code that will join two arrays by comparing the id property of each element.

 //dummy test data var arrayA = [{ id: 0, data: "hello" }, { id: 1, data: "world" }, { id: 2, data: "!" }], arrayB = [{ id: 2, data2: "bbb" }, { id: 1, data2: "aaa" }, { id: 3, data2: "ccc" }]; (function(a, b) { for (var i in a) { var pos = b.findIndex(function(obj) { return a[i].id == obj.id; }); if (pos !== -1) { a[i] = $.extend({}, a[i], b[pos]); } else { //do something } } })(arrayA, arrayB); console.log(arrayA); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 

As you can see, when tested in Chrome dev tools, the code works perfectly fine. However, ESLint keeps telling me that function in a loop can lead to wrong output ( no-loop-func ). So, the question is, is it safe to use the loop variable in the anonymous function inside Array.prototype.findIndex() or find() ? If not, under what circumstance could the code go wrong?

What you are asking is explained in the article you've mentioned. In short: it is safe in your case, but may not become safe if you later modify that code.

I would suggest to rewrite your code into two loops, as it is easier to understand, safer and more performant.

for (var i = 0; i < a.length; i++) {
    var found = false;

    for (var j = 0; j < b.length; j++) 
        if (a[i].id === b[j].id) {
            Object.assign(a[i], b[j]);
            found = true;
            break;
        }

    if (!found)
        ...
 }

Alternate method without the found variable (which to me is kind of hacky):

for (var i = 0; i < a.length; i++) {

    for (var j = 0; j < b.length; j++) 
        if (a[i].id === b[j].id)
            break;

    if (j < b.length) {
        // Found a match
        Object.assign(a[i], b[j]);
        ...
    } else {
        ...
    }
 }

Simplicity of the code is important and nested functions in loops usually tend to be less perfromant, readable and confusing (for others or for you after some time).

is it safe to use the loop variable in the anonymous function inside Array.prototype.findIndex() or find() ? If not, under what circumstance could the code go wrong?

Yes, it is safe and this is because the callback passed to findIndex is executed synchronously (the same is true for find ). The lint rule makes sense since in the general case a callback function passed to some method could be called asynchronously, in which case it is a problem. That problem could then be resolved by declaring i with block scope:

for (let i in a) {

This change may even silence the linter for your case.

Remarks

Note that you can do the job without having to scan the b array with findIndex (or find ) in every iteration, and so make this run with O(n) instead of O(n²) time complexity, if you would use a Map of some sort.

Also, it is generally not recommended to iterate over an array with the for...in syntax. In your case you could use the forEach array method.

Here is how that could look with some ES6 code:

(function(a, b) {
    const b_ids = new Map(b.map(obj => [obj.id, obj]));
    a.forEach( (obj, i) => {
        if (b_ids.has(obj.id)) {
            a[i] = Object.assign({}, obj, b_ids.get(obj.id));
        } else {
            //do something
        }
    });
})(arrayA, arrayB);

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