1

I'd like to filter this data for unique sub-arrays with the second property, so

var arr = [
    ["foo", "one"],
    ["bar", "one"],
    ["baz", "two"],
    ["qux", "two"]
]

should turn into

var arr = [
    ["foo", "one"],
    ["baz", "two"]
]

I could try a manual approach with two nested forEach, but it seems a inefficient. Array.prototype.filter seems to be better suited, but I fear I'm lacking experience to use it properly.

1

5 Answers 5

2

Another version (assuming your definition of arr exists):

arr = arr.filter(function(innerArr){
  return !this[innerArr[1]] && (this[innerArr[1]]=true)
},{})
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks let me know if you need further explanation. :).
@silverwind, @mattyice A quick test is telling me that whatever you are assigning to with the this[...]=true expression exists in neither the original nor the resulting array, but rather in the global object. So it appears that you could substitute this with either window or self and have the same result of polluting or overwriting global variables (at least in the environment I tested it on).
hi, please refer to the docs here: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…. I am passing an empty object as the thisArg and using that as my storage to track what I've seen. Therefore this in this case is actually a temp object used in the filter function only, not global or window
@mattyice Okay, gotcha. It's that optional parameter I always seem to ignore.
yes, from that doc i referenced: If a thisArg parameter is provided to filter, it will be passed to callback when invoked, for use as its this value. Otherwise, the value undefined will be passed for use as its this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function.
1
  1. Store the array which you want to keep against the second element of the array, in an Object. This is to avoid duplicates.

    var obj = {};
    
    arr.forEach(function(currentArray) {
        if (obj.hasOwnProperty(currentArray[1]) === false) {
            obj[currentArray[1]] = currentArray;
        }
    });
    
  2. And then collect the values alone, like this

    var result = [];
    for (var key in obj) {
        result.push(obj[key]);
    }
    
    console.log(result);
    # [ [ 'foo', 'one' ], [ 'baz', 'two' ] ]
    

The run time of this solution is in O(N).

Note: This solution may not gurantee the order of the items in the result.

1 Comment

I don't really care about ordering in this case, so this looks nice and readable.
1

The following does what you want I think. http://jsfiddle.net/pitaj/ktj06Ljt/

var seenit = []; // array to store properties already found

var newarr = arr.filter(function(it){ 
  var result = seenit.indexOf(it[1]) === -1; // check if already found
  seenit.push(it[1]); // add to found array
  return result; // return if it was found, as per filter
});

This way will keep them in the same order as the original.

EDIT: alternative, you can do it without the external array (as Cheery pointed out):

var newarr = arr.filter(function(it){ 
  var result = this.indexOf(it[1]) === -1;
  this.push(it[1]); 
  return result;
}, []);

http://jsfiddle.net/jdLz4gm7/

Comments

1

Maybe something like this:

var arr = [
    ["foo", "one"],
    ["bar", "one"],
    ["baz", "two"],
    ["qux", "two"]
];
var uniques=[
  Object.create(null),
  Object.create(null)
];
var result=arr.filter(function(item_set){
  return item_set.every(function(item,idx){
    var u=!uniques[idx][item];
    uniques[idx][item]=true;
    return u;
  });
});

/*
result=[["foo", "one"], ["baz", "two"]];
*/

* correction: changed uniques[item] to uniques[idx][item] so each item of the pair accesses its own lookup table.

Comments

0

With underscore.js, you can just type code below:

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]

Update:

I'm so sorry about that i misread your question, The answer above is wrong.
I write a new demo for your question: http://jsfiddle.net/abruzzi/0an5yr9c/2/
and here are codes below:

var arr = [
    ["foo", "one"],
    ["bar", "one"],
    ["baz", "two"],
    ["qux", "two"]
];

var uniq = [];
var result = arr.filter(function(sub_arr){
    return sub_arr.every(function(sub_item){
        if(uniq.indexOf(sub_item) < 0) {
            uniq.push(sub_item);
            return true;
        }
        return false;
    });
})

result => [
    ["foo", "one"],
    ["baz", "two"]
]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.