7
\$\begingroup\$

I have the following script, which filters duplicates and returns only unique values for an array. When passing false or nothing the "original" array is overwritten, when passing true a new array is created with unique values and returned instead.

The script should work only with numbers and strings.

I would like to know:

  • If it is possible to optimize the code for maximum performance and how,
  • If it is possible to improve or write in alternative way logic for createArray, maybe a better way in term of code repetition.

https://jsfiddle.net/dj7qxyL7/

    Array.prototype.getUnique = function (createArray) {
        createArray = createArray === true ? true : false;
        var temp = JSON.stringify(this);
        temp = JSON.parse(temp);
        if (createArray) {
            var unique = temp.filter(function (elem, pos) {
                return temp.indexOf(elem) == pos;
            }.bind(this));
            return unique;
        }
        else {
            var unique = this.filter(function (elem, pos) {
                return this.indexOf(elem) == pos;
            }.bind(this));
            this.length = 0;
            this.splice(0, 0, unique);
        }
    }

    var duplicates = [0, 0, 1, 1, 2, 3, 1, 1, 0, 4, 4];
    console.log('++++ ovveride')
    duplicates.getUnique();
    console.log(duplicates);
    console.log('++++ new array')
    var duplicates2 = [0, 0, 1, 1, 2, 3, 1, 1, 0, 4, 4];
    var unique = duplicates2.getUnique(true);
    console.log(unique);
    console.log('++++ original')
    console.log(duplicates2);
\$\endgroup\$
10
\$\begingroup\$

If is possible to improve or write in alternative way logic for createArray, maybe a better way in term of code repetition.

Why give the option to mutate the array? Most array methods return a new array, including filter, so it seems redundant in your code, since you get a new array anyway. Not mutating the array is usually the right thing to do. In any case, using JSON to clone an array is very inefficient, when all you need is var copy = array.slice(); and a recursive solution for nested arrays. If you get rid of this unusual requirement, and create your own function instead of extending the prototype, then you are left with what unique really is:

var unique = function(xs) {
  return xs.filter(function(x, i) {
    return xs.indexOf(x) === i
  })
}

If it is possible to optimize the code for maximum performance and how

If you only deal with things that can be stringified properly, like numbers, and obviously strings, then you can improve performance by using a simple object to keep track of items that have been seen already:

var unique = function(xs) {
  var seen = {}
  return xs.filter(function(x) {
    if (seen[x])
      return
    seen[x] = true
    return x
  })
}

Although this would consider 1 and '1' to be a duplicate.

\$\endgroup\$
  • \$\begingroup\$ Would you suggest me to have a prototype extension instead of a "utility" function? Thanks for your time on this, I very appreciate it. \$\endgroup\$ – GibboK Mar 10 '15 at 9:29
  • 3
    \$\begingroup\$ I would suggest to never extend native prototypes. But also it makes the function less reusable if you want to pass it around as a function, instead of calling dot on a array, then it becomes a mess with Function.prototype.call.bind(Array.prototype.unique)(xs). Not so good for composition. \$\endgroup\$ – elclanrs Mar 10 '15 at 9:32
  • \$\begingroup\$ How would we modify this code to compare if 2 columns are the same? 1 column being the same isn't enough for my use case. \$\endgroup\$ – sojim2 Aug 24 at 18:37
2
\$\begingroup\$

In addition to @elclanrs' excellent review, there are a few minor coding style issues I suggest to fix too.

Instead of this:

    createArray = createArray === true ? true : false;

You can (and should) write simply this:

    createArray = createArray === true;

And since the temp variable is only used when createArray is true, you should move that inside the if (createArray) to avoid pointless executions.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.