Вопрос по jquery, javascript, arrays – Поиск совпадений между несколькими массивами JavaScript

50

У меня есть несколько массивов со строковыми значениями, и я хочу сравнить их и сохранить только совпадающие результаты, которые совпадают междуALL из них.

Учитывая этот пример кода:

var arr1 = ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
var arr2 = ['taco', 'fish', 'apple', 'pizza'];
var arr3 = ['banana', 'pizza', 'fish', 'apple'];

Я хотел бы создать следующий массив, который содержит совпадения из всех заданных массивов:

['apple', 'fish', 'pizza']

Я знаю, что могу объединить все массивы сvar newArr = arr1.concat(arr2, arr3); но это просто дает мне массив со всем, плюс дубликаты. Может ли это быть легко сделано без необходимости использования дополнительных библиотек, таких как underscore.js?

(Отлично, иnow я тоже голоден!)

EDIT Полагаю, я должен упомянуть, что может быть неизвестное количество массивов, я просто использовал 3 в качестве примера.

Взгляните на этоstackoverflow.com/questions/1885557/… nbrooks

Ваш Ответ

9   ответов
2

мышления:

Calculate join/intersection between two arrays

var arrA = [1,2,3,4,5];
var arrB = [4,5,10];
var innerJoin = arrA.filter(el=>arrB.includes(el));
console.log(`Intersection is: ${innerJoin}`);

Reduce the content: calculate the intersection between the acumulated intersection and the next array.

var arrays = [
 ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'],
 ['taco', 'fish', 'apple', 'pizza'],
 ['banana', 'pizza', 'fish', 'apple']
];
var join = arrays.reduce((join, current) => join.filter(el => current.includes(el)));
console.log(`Intersection is: ${join}`);

0

function getCommon(a) {

  // default result is copy of first array
  var result = a[0].slice();
  var mem, arr, found = false;

  // For each member of result, see if it's in all other arrays
  // Go backwards so can splice missing entries
  var i = result.length;

  while (i--) {
    mem = result[i];

    // Check in each array
    for (var j=1, jLen=a.length; j<jLen; j++) {
      arr = a[j];
      found = false;

      // For each member of arr and until found
      var k = arr.length;
      while (k-- && !found) {

        // If found in this array, set found to true
        if (mem == arr[k]) {
          found = true;
        }
      }
      // if word wasn't found in this array, remove it from result and 
      // start on next member of result, skip remaining arrays.
      if (!found) {
        result.splice(i,1);
        break;
      }
    }
  }
  return result;
}

var data = [
  ['taco', 'fish', 'apple', 'pizza', 'mango', 'pear'],
  ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'],
  ['banana', 'pizza', 'fish', 'apple'],
  ['banana', 'pizza', 'fish', 'apple', 'mango', 'pear']
];
Edit

Функция для поиска никогда не перечисляемых свойств, основанных на этом на Object.prototype:

// Return an array of Object.prototype property names that are not enumerable
// even when added directly to an object.
// Can be helpful with IE as properties like toString are not enumerable even
// when added to an object.
function getNeverEnumerables() {

    // List of Object.prototype property names plus a random name for testing
    var spNames = 'constructor toString toLocaleString valueOf ' +
                  'hasOwnProperty isPrototypeOf propertyIsEnumerable foo';

    var spObj = {foo:'', 'constructor':'', 'toString':'', 'toLocaleString':'', 'valueOf':'',
                 'hasOwnProperty':'', 'isPrototypeOf':'', 'propertyIsEnumerable':''};

    var re = [];

    // BUild list of enumerable names in spObj
    for (var p in spObj) {
      re.push(p); 
    }

    // Remove enumerable names from spNames and turn into an array
    re = new RegExp('(^|\\s)' + re.join('|') + '(\\s|$)','g');
    return spNames.replace(re, ' ').replace(/(^\s+)|\s\s+|(\s+$)/g,'').split(' ');
}

document.write(getNeverEnumerables().join('<br>'));
1

function intersection(arr1, arr2) {
  var temp = [];

  for (var i in arr1) {
    var element = arr1[i];

    if (arr2.indexOf(element) > -1) {
      temp.push(element);
    }
  }

  return temp;
}

function multi_intersect() {
  var arrays = Array.prototype.slice.apply(arguments).slice(1);
  var temp = arguments[0];

  for (var i in arrays) {
    temp = intersection(arrays[i], temp);

    if (temp == []) {
      break;
    }
  }

  return temp;
}

var arr1 = ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
var arr2 = ['taco', 'fish', 'apple', 'pizza'];
var arr3 = ['banana', 'pizza', 'fish', 'apple'];

multi_intersect(arr1, arr2, arr3);
9

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

var arr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7]],
    int = arr.reduce((p,c) => p.filter(e => c.includes(e)));

document.write("<pre>" + JSON.stringify(int) + "</pre>");

Идеально - для таких вещей всегда есть 1 лайнер. Мое единственное предложение - дать более значимые имена парамам, потому что я не могу сказать, чтоp, c а такжеe представлять.
Я понятия не имею, почему это не правильный ответ - я знаю; был дан через 4 года ... Но так и должно быть! Tx @Redu!
@ Эдмунд Рид Спасибо ... Переменныеp для предыдущего иc для тока обычны дляreduce операции без начального значения.e Элемент for является очень распространенным именем переменной для всех обратных вызовов методов массива.
12

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

Преимущества этого подхода:

~15x faster that brute force search options (used by other answers) if arrays are larger Does not require ES5 or ES5 shim (works with all browsers) Completely non-destructive (doesn't change source data at all) Handles duplicates items in source arrays Handles an arbitrary number of input arrays

А вот и код:

function containsAll(/* pass all arrays here */) {
    var output = [];
    var cntObj = {};
    var array, item, cnt;
    // for each array passed as an argument to the function
    for (var i = 0; i < arguments.length; i++) {
        array = arguments[i];
        // for each element in the array
        for (var j = 0; j < array.length; j++) {
            item = "-" + array[j];
            cnt = cntObj[item] || 0;
            // if cnt is exactly the number of previous arrays, 
            // then increment by one so we count only one per array
            if (cnt == i) {
                cntObj[item] = cnt + 1;
            }
        }
    }
    // now collect all results that are in all arrays
    for (item in cntObj) {
        if (cntObj.hasOwnProperty(item) && cntObj[item] === arguments.length) {
            output.push(item.substring(1));
        }
    }
    return(output);
}    

Рабочая демонстрация:http://jsfiddle.net/jfriend00/52mAP/

К вашему сведению, это не требует ES5, поэтому будет работать во всех браузерах без прокладки.

В тесте производительности на 15 массивах, каждый длиной 1000, это было более чем в 10 раз быстрее, чем метод поиска, используемый в ответе я не в этом jsperf:http://jsperf.com/in-all-arrays.

Вот версия, которая использует ES6Map а такжеSet дедуплицировать и отслеживать счет. Преимущество этого заключается в том, что тип данных сохраняется и может быть любым (ему даже не нужно иметь естественное преобразование строк, данные могут даже быть объектами, хотя объекты сравниваются на предмет того же самого объекта, не имея того же самого). свойства / значения).

var arrays = [
    ['valueOf', 'toString','apple', 'orange', 'banana', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza', 1, 2, 999, 888],
    ['valueOf', 'toString','taco', 'fish', 'fish', 'apple', 'pizza', 1, 999, 777, 999, 1],
    ['valueOf', 'toString','banana', 'pizza', 'fish', 'apple', 'apple', 1, 2, 999, 666, 555]
    ];
    
// subclass for updating cnts    
class MapCnt extends Map {
    constructor(iterable) {
        super(iterable);
    }
    
    cnt(iterable) {
        // make sure items from the array are unique
        let set = new Set(iterable);
        // now update the cnt for each item in the set
        for (let item of set) {
            let cnt = this.get(item) || 0;
            ++cnt;
            this.set(item, cnt);
        }
    }
}


function containsAll(...allArrays) {
    let cntObj = new MapCnt();
    for (array of allArrays) {
        cntObj.cnt(array);
    }
    // now see how many items have the full cnt
    let output = [];
    for (var [item, cnt] of cntObj.entries()) {
        if (cnt === allArrays.length) {
            output.push(item);
        }
    }
    return(output);
}    

var result = containsAll.apply(this, arrays);

document.body.innerHTML = "<pre>[<br>    " + result.join(',<br>    ') + "<br>]</pre>";

О, одна проблема с этим подходом состоит в том, что в IE 8 и ниже, по крайней мере, свойства "toString" и & quot; valueOf & quot; всегда не перечисляются, поэтому, если рассматриваемые массивы имеют эти имена в качестве значений элементов, вышеприведенное никогда не поместит их в массив результатов. Одним из решений является проверка этих значений наitem в явном виде.
@RobG - я изменил код для работы с"toString", "valueOf" в IE8 или любой другой встроенный метод. Для этого я добавляю префикс к каждому ключу, чтобы отличать его от любых встроенных методов.
& # x2014; другой подход - добавить тест свойств Object.prototype для простого объекта, чтобы увидеть, какие из них никогда не перечисляются, а затем проверить их после for..in в конце.
+1 мне нравитсяcontainsAll подход, я думал об объектно-ориентированном подходе, но не с использованием счетчика. Хорошая обработка дупов, не удаляя их из исходного массива тоже. Я думаю, что большая скорость достигается за счет отказа от методов массива, таких как сплайсинг и срез, поиск свойств объекта, вероятно, очень высоко оптимизирован, поскольку он настолько важен для любого нетривиального скрипта.
Доработан алгоритм обработки ошибок и добавлен тест производительности, чтобы показать, насколько он быстрее, чем некоторые другие методы (в 14 раз быстрее).
0

	 // Intersect any number of arrays:

	function intersect() {

	  // - Arguments -> traditional array,
	  // - First item ( arrays[0] ) = shortest to reduce iterations
	  var arrays = Array.prototype.slice.call(arguments).sort(function(a, b) {
	    return a.length - b.length;
	  });

	  // Use first array[0] as the base.
	  var a = arrays.shift();

	  var result = [];
	  for (var i = a.length; i--;) {

	    var val = a[i];

	    // Prevent duplicates
	    if (result.indexOf(val) < 0) {

	      // Seek
	      var found = true;
	      for (var ii = arrays.length; ii--;) {
	        if (arrays[ii].indexOf(val) < 0) {
	          found = false;
	          break;
	        }
	      }

	      if (found) {
	        result.push(val);
	      }

	    }

	  }

	  return result;

	}

	/*
	// Slower, but smaller code-base:
	function intersect (){
		
		// - Arguments -> traditional array,
		// - First item ( arrays[0] ) = shortest to reduce iterations
		var arrays = Array.prototype.slice.call(arguments).sort(function(a, b) {
	        return a.length - b.length;
	    });
		
		// Use first array[0] as the base.
		var a = arrays.shift();

		return a.filter(function (val, idx, aa) {
			
						// Seek
		                for(var i=arrays.length; i--;){
		                    if (arrays[i].indexOf(val) < 0) {
							    return false;
						    }
		                }
						
						// Prevent duplicates
		                return aa.indexOf(val) === idx;
		
					});

	}
	*/

	var arr1 = ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
	var arr2 = ['taco', 'fish', 'apple', 'pizza', 'apple', 'apple'];
	var arr3 = ['banana', 'pizza', 'fish', 'apple', 'fish'];

	var arr1 = ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
	var arr2 = ['taco', 'fish', 'apple', 'pizza', 'apple', 'apple'];
	var arr3 = ['banana', 'pizza', 'fish', 'apple', 'fish'];


	var result = intersect(arr1, arr2, arr3);

	 // For fiddle output:
	var elem = document.getElementById("result");
	elem.innerHTML = JSON.stringify(result);
	console.log(result);
<div id="result">Results</div>

2

DEMO:http://jsfiddle.net/qUQHW/

var tmp = {};
for (i = 0; i < data.length; i++) {
    for (j = 0; j < data[i].length; j++) {
        if (!tmp[data[i][j]]) {
            tmp[data[i][j]] = 0;
        }
        tmp[data[i][j]]++;
    }
}

var results = $.map(tmp, function(val,key) {
    return val == data.length ? key :null;
})
Он не самый читаемый, но он работает. +1
80
var result = arrays.shift().filter(function(v) {
    return arrays.every(function(a) {
        return a.indexOf(v) !== -1;
    });
});

DEMO: http://jsfiddle.net/nWjcp/2/

Вы можете сначала отсортировать внешний массив, чтобы получить самый короткий массив в начале ...

arrays.sort(function(a, b) {
    return a.length - b.length;
});

Для полноты, вот решение, которое имеет дело с дубликатами в массивах. Оно использует.reduce() вместо.filter()...

var result = arrays.shift().reduce(function(res, v) {
    if (res.indexOf(v) === -1 && arrays.every(function(a) {
        return a.indexOf(v) !== -1;
    })) res.push(v);
    return res;
}, []);

DEMO: http://jsfiddle.net/nWjcp/4/

@Derek :: Это было относительно недавнее открытие и для меня. Проверьте MDN документы. Второй аргумент действительно ловкий. Кроме того, вам не нужно передавать номер. Это может быть строка, которая будет использоваться в качестве символа отступа.
@ChrisBarr: Чтобы охватить все базы, я добавил решение в том же стиле, которое работает с дубликатами. Это внизу.
@amnotiam Это правила, большое спасибо! Мне действительно нужно больше узнать об этих встроенных методах, они мощные. Chris Barr
@amnotiam да, они вложены в другой массив. Я думаю, мне нужно поправиться и дать пример кода ... ха! Chris Barr
@ TomB .: Вы хотите1) хранить элементы из массивов, которые не отображаются вany других массивов или2) сохранить элементы, которые не отображаются вat least one из других массивов? Так что, если "пицца" был в первом и втором массивах, но не в третьем, это было быnot быть включены в соответствии с толкованием1 но будет в соответствии с2.
3

иве, и предотвратить дубликаты в возвращенном массиве.

function arraysInCommon(arrays){
    var i, common,
    L= arrays.length, min= Infinity;
    while(L){
        if(arrays[--L].length<min){
            min= arrays[L].length;
            i= L;
        }
    }
    common= arrays.splice(i, 1)[0];
    return common.filter(function(itm, indx){
        if(common.indexOf(itm)== indx){
            return arrays.every(function(arr){
                return arr.indexOf(itm)!= -1;
            });
        }
    });
}

var arr1= ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
var arr2= ['taco', 'fish', 'apple', 'pizza', 'apple','apple'];
var arr3= ['banana', 'pizza', 'fish', 'apple','fish'];

var allArrays = [arr1,arr2,arr3];

arraysInCommon(allArrays).sort();

возвращаемое значение:apple,fish,pizza

DEMO - http://jsfiddle.net/kMcud/

Похожие вопросы