Как использовать "пересечение" подчеркивания объектов?

_.intersection([], [])

работает только с примитивными типами, правильно?

Он не работает с объектами. Как я могу заставить его работать с объектами (возможно, проверив поле "Id" )?

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ]
var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ]

В этом примере результат должен быть:

_.intersection(a, b);
  

[{'id': 1, 'name': 'jake'}];

  

Ответ 1

Вы можете создать другую функцию, основанную на функции подчеркивания. Вам нужно только изменить одну строку кода из исходной функции:

_.intersectionObjects = 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); });
      });
    });
  };

В этом случае вы теперь будете использовать метод underscore isEqual() вместо сопоставления равенства JavaScript. Я попробовал это с вашим примером, и это сработало. Вот выдержка из документации подчеркивания относительно функции isEqual:

_.isEqual(object, other) 
Performs an optimized deep comparison between the two objects, to determine if they should be considered equal.

Здесь вы можете найти документацию: http://documentcloud.github.com/underscore/#isEqual

Я выставляю код на jsFiddle, чтобы вы могли проверить и подтвердить его: http://jsfiddle.net/luisperezphd/jrJxT/

Ответ 2

Вот альтернативный алгоритм, который должен быть гибким и работать лучше. Одним из таких улучшений является то, что вы можете указать свою собственную функцию сравнения, поэтому в вашем случае вы можете просто сравнить идентификатор, если это уникальный идентификатор.

function intersectionObjects2(a, b, areEqualFunction) {
    var results = [];

    for(var i = 0; i < a.length; i++) {
        var aElement = a[i];
        var existsInB = _.any(b, function(bElement) { return areEqualFunction(bElement, aElement); });

        if(existsInB) {
            results.push(aElement);
        }
    }

    return results;
}

function intersectionObjects() {
    var results = arguments[0];
    var lastArgument = arguments[arguments.length - 1];
    var arrayCount = arguments.length;
    var areEqualFunction = _.isEqual;

    if(typeof lastArgument === "function") {
        areEqualFunction = lastArgument;
        arrayCount--;
    }

    for(var i = 1; i < arrayCount ; i++) {
        var array = arguments[i];
        results = intersectionObjects2(results, array, areEqualFunction);
        if(results.length === 0) break;
    }

    return results;
}

Вы можете использовать его следующим образом:

var a = [ { id: 1, name: 'jake' }, { id: 4, name: 'jenny'} ];
var b = [ { id: 1, name: 'jake' }, { id: 9, name: 'nick'} ];
var c = [ { id: 1, name: 'jake' }, { id: 4, name: 'jenny'}, { id: 9, name: 'nick'} ];

var result = intersectionObjects(a, b, c, function(item1, item2) {
    return item1.id === item2.id;
});

Или вы можете оставить эту функцию, и она будет использовать функцию подчеркивания _.isEqual(), например:

var result = intersectionObjects(a, b, c);

Вы можете найти его на jsFiddle здесь: http://jsfiddle.net/luisperezphd/43vksdn6/

Ответ 3

Методы массива в подчеркивании очень мощные, вам нужно всего несколько строк, чтобы выполнить то, что вы хотите сделать:

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ];
var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];

var result = _(a).chain().map(function(ea) {
    return _.find(b, function(eb) {return ea.id == eb.id;});
}).compact().value();

Если у вас есть большие массивы, вы можете избавиться от вызова compact() с помощью одной дополнительной строки:

var result = [];
_.each(a, function(ea) {
    var entry = _.find(b, function(eb) {return ea.id == eb.id;});
    if (entry) result.push(entry);
});

Ответ 4

Я хотел бы поделиться своим общим решением для этих случаев.

Я добавил общую функцию для подчеркивания, используя mixin, которая выполняет двоичную операцию "array" в двух коллекциях в соответствии с заданной функцией Hash:

_.mixin({
    collectionOperation: function(arr1, arr2, hash, action) {
        var iArr1 = _(arr1).indexBy(hash)
            , iArr2 = _(arr2).indexBy(hash);
        return action(_(iArr1).keys(), _(iArr2).keys()).map(function (id) {
            return iArr1[id] || iArr2[id];
        });
    }
});

Пример использования:

_([{id:1,v:'q'},{id:2,v:'p'}]).collectionOperation([{id:3,v:'pq'}], 'id', _.union )

Обратите внимание, что 'id' может быть заменен функцией.

Я считаю, что это решение O (n + m).

Ответ 5

Технически, он работает над объектами, но вам нужно быть осторожным с ссылочным равенством.

var jake = {'id': 1, 'name': 'jake' },
    jenny = {'id':4, 'name': 'jenny'},
    nick =  {'id': 9, 'name': 'nick'};
var a = [jake, jenny]
var b = [jake, nick];

_.intersection(a, b);
// is
[jake]

Ответ 6

В lodash 4.0.0. Мы можем попробовать это

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ];
var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];

_.intersectionBy(a, b, 'id');

Вывод:

[{'id': 1, 'name': 'jake'}];

Ответ 7

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ];
var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];

Рабочая функция:

 function intersection(a,b){
  var c=[];
   for(m in a){
      for(n in b){
         if((a[m].id==a[n].id)&&(a[m].name==b[n].name))
                 c.push(a[m]);          
      }}
    return c;
  }
console.log(intersection(a,b));

Я также пробовал код в jQuery специально после предложения Pointy. Сравнение должно быть настроено в соответствии с структурой объекта JSON.

<script type="text/javascript">
jQuery(document).ready(function(){
    var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ];
    var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];
    var c=[];
    jQuery.each(a, function(ka,va) {
       jQuery.each(b, function(kb,vb) {      
                if(compare(va,vb))
                    c.push(va); 
     });   
    });
     console.log(c);  
});
function compare(a,b){
  if(a.id==b.id&&a.name==b.name)
     return true;
  else return false;
}
</script>

Ответ 8

Если вы хотите сравнить только объекты:

b = {"1":{"prod":"fibaro"},"2":{"prod":"aeotec"},"3":{"prod":"sw"}}; 
a = {"1":{"prod":"fibaro"}};


_.intersectObjects = function(a,b){
    var m = Object.keys(a).length;
    var n = Object.keys(b).length;
    var output;
    if (m > n) output = _.clone(a); else output = _.clone(b);

    var keys = _.xor(_.keys(a),_.keys(b));
    for(k in keys){
        console.log(k);
        delete output[keys[k]];
    }
    return output;
}
_.intersectObjects(a,b); // this returns { '1': { prod: 'fibaro' } }

Ответ 9

//nested array is in the format of [[],[],[]]

function objectArrayIntersection(nestedArrays){     

    let intersectingItems = [];                
    let uniqArr = _.uniq(_.flatten(nestedArrays)); //intersecting items removed    
    const countOfNestedArrays = nestedArrays.length;


    for (let index = 0; index < uniqArr.length; index++) {
        let uniqItem = uniqArr[index];
        let foundCount = 0;

        for(var j = 0;j<countOfNestedArrays;j++){
            var i = _.indexOf(nestedArrays[j],uniqItem);
            if(i != -1)
                foundCount ++;
        }

        if(foundCount ==  countOfNestedArrays){
            intersectingItems.push(uniqItem);
        }
    }

    return intersectingItems;
}

Я пытался решить это таким образом.

Ответ 10

var a = {a:'a1',b:'b1'},
    b = {a:'a2',b:'b2',c:'c2'};

_.pick(a,_.intersection(_.keys(a),_.keys(b)));

// {a:'a1',b:'b1'}