Question

I can't figure out how to find the intersection of this set of arrays:

[
 [
  {"name":"product1","light":"1"},
  {"name":"product2","light":"2"},
  {"name":"product5","light":"5"},
  {"name":"product4","light":"4"}
 ],
 [
  {"name":"product2","light":"2"},
  {"name":"product3","light":"3"},
  {"name":"product4","light":"4"}
 ],[...more arrays with objects]
]

This is just sample data, the real set I have changes a lot but with that structure. I want the returned intersection to look like this (a single array of the intersected objects):

[
 {"name":"product2","light":"2"},
 {"name":"product4","light":"4"},
]

I tried this together with LoDashjs and Underscorejs:

_.intersectionObjects = _.intersect = function(array) {
var slice = Array.prototype.slice; // added this line as a utility
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
  return _.every(rest, function(other) {
    //return _.indexOf(other, item) >= 0;
    return _.any(other, function(element) { return _.isEqual(element, item); });
  });
});
};

I need this because I am trying to create a tags system using knockoutjs. I have a layout of categorized tag buttons that write to a "filter" observable array on click, the only thing left is to find the intersection of the filtered products that are contained in this observable array.

Please help me out, I've been trying to solve this for two days straight but lack the javascript knowledge to figure it out. Thanks in advance!

Was it helpful?

Solution

try adding they apply method:

  var myArr = [
    [
      {"name":"product1","light":"1"},
      {"name":"product2","light":"2"},
      {"name":"product5","light":"5"},
      {"name":"product4","light":"4"}
    ],
    [
      {"name":"product2","light":"2"},
      {"name":"product3","light":"3"},
      {"name":"product4","light":"4"}
    ]
  ]

  _.intersectionObjects = _.intersect = function(array) {
    var slice = Array.prototype.slice;
    var rest = slice.call(arguments, 1);
    return _.filter(_.uniq(array), function(item) {
      return _.every(rest, function(other) {
        return _.any(other, function(element) {
          return _.isEqual(element, item);
        });
      });
    });
  };

  var myIntersection = _.intersectionObjects.apply(_, myArr);

  for (var i = 0; i < myIntersection.length; i++) {
    console.log(myIntersection[i]);
  }

  // Sample Output:
  // Object {name: "product2", light: "2"}
  // Object {name: "product4", light: "4"}

OTHER TIPS

Here's a method I used that seems to work well.

var arr1 = [{"id":"1"},{"id":"2"},{"id":"3"}];
var arr2 = [{"id":"1"},{"id":"3"}];

function match(item){
var isMatch = _.matcher(item);
var matches = _.filter(arr2, isMatch);
  return matches[0];
}

var matchArray = _.compact(_.map(arr1, function(val){ return match(val)}));

document.write(JSON.stringify(matchArray));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

You are likely to run across errors if you're comparing just the objects themselves, as this will return false:

var o1 = {"foo":"bar"};
var o2 = {"foo":"bar"};
return o1 == o2;

You'll need to compare the values inside of the objects, and intersect based on those:

The JSFiddle here does what you like. http://jsfiddle.net/turiyag/bWrQW/6/

function valueIntersect(a1, a2) {
    var aReturn = [];
    for(i1 in a1) {
        o1 = a1[i1];
        for (i2 in a2) {
            o2 = a2[i2];
            if(equals(o1,o2)) {
                aReturn.push(o1);
                break;
            }
        }
    }
    return aReturn;
}

function equals(o1, o2) {
    if (!o2 || o1.length != o2.length) {
        return false;
    }
    for (i in o1) {
        if (o1[i] !== o2[i]) {
            return false;
        }
    }
    return true;
};

as per https://lodash.com/docs#intersectionBy

_.intersectionBy([arrays], [iteratee=_.identity])

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top