Comparing two arrays to see if they contain objects with different IDs

  • This feels like the kind of problem that has an extremely elegant solution. But my code is mechanical and straightforward. Not bad but I'm interested if there's something nicer.



    Performance is not really an issue. I'm looking for elegant/shorter, not faster.



    No underscore.js please; it's not worth fighting my coworkers to get another third-party library in there, especially when it duplicates so much ES5. Speaking of which, we work in an ES5-only environment so use all the array extras you want.



    function getIds(array) {
    // We have added `Array.prototype.unique`; it does what you'd expect.
    return array.map(function (x) { return x.id; }).unique().sort();
    }

    function areDifferentByIds(a, b) {
    var idsA = getIds(a);
    var idsB = getIds(b);

    if (idsA.length !== idsB.length) {
    return true;
    }

    for (var i = 0; i < idsA.length; ++i) {
    if (idsA[i] !== idsB[i]) {
    return true;
    }
    }

    return false;
    }

    In general you want to name it `containSameIds` and just swap `return true` with `return false`. I personally would convert arrays to hash sets and then iterate over one, then another (if sizes are the same). I would not try to make the function too short at the expense of readability.

    I would remove the .length comparison code. The for loop below it, would give the same result. The only reason for putting it there would be performance.

    @Gerben consider `idsA = ["x"]`, `idsB = ["x", "y"]`.

    Consider adding Array.prototype.compare too. :)

    @Domenic I see. In that case `!==` should be `<` :-P

  • Gerben

    Gerben Correct answer

    9 years ago
    function areDifferentByIds(a, b) {
    var idsA = a.map( function(x){ return x.id; } ).unique().sort();
    var idsB = b.map( function(x){ return x.id; } ).unique().sort();
    return (idsA.join(',') !== idsB.join(',') );
    }


    This is the simplest I could make it. I think such small functions need to be as self contained as possible, and not need other functions. This makes them more readable, maintainable, and of course reusable. I therefor removed the getIds function. If you use that elsewhere you can keep it.



    You might want to store the function (x) { return x.id; } inside a variable, and reuse it on both maps



    I also think it is faster, because join is quite fast, and it doesn't require a for-loop.



    EDIT my next attempt



    function areDifferentByIds(a, b) {
    var idsA = a.map( function(x){ return x.id; } ).unique();
    var idsB = b.map( function(x){ return x.id; } ).unique();
    var idsAB = a.concat(b).unique();
    return idsAB.length!==idsA.length
    }


    secondly, you could make it more generic, by adding the property you want to check to the arguments of the function. Something like:



    function areDifferentByProperty(a, b, prop) {
    var idsA = a.map( function(x){ return x[prop]; } ).unique();
    var idsB = b.map( function(x){ return x[prop]; } ).unique();
    var idsAB = a.concat(b).unique();
    return idsAB.length!==idsA.length
    }


    thirdly, I would much rather name it areEqualBy___. Seems more intuitive to me, but that's just me.


    No good, my IDs can be arbitrary strings. `idsA = ["x,y"]` vs `idsB = ["x", "y"]` breaks it.

    And I'm saying right now, any solutions that rely on picking an "unguessable" sequence of Unicode characters to be the joiner will probably work in the real world, but are even less elegant than my original ;)

    I think less code is more elegant, but I guess you are right. If id's can be arbitrary strings, it could cause in incorrect result, which is not very elegant, to say the least.

    I made another attempt, that should work regardless of the id format. See edit above.

    OK the second attempt is really clever and very much along the lines of what I'm looking for. We have a winner!!!

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM

Tags used