Найпростіший код для перетину масиву в JavaScript


607

Який найпростіший, без бібліотеки код для реалізації перетинів масивів у javascript? Я хочу написати

intersection([1,2,3], [2,3,4,5])

і дістати

[2, 3]

16
Хочете просто чи швидко?
СЛАкс

11
Пріоритет простий, але він не може бути настільки загиблим у мозку, що це буде виступ зі спектаклем :)
Пітер

4
Я створив тестову сторінку JsFiddle Banchmark для всіх методів тут, включаючи функцію перекреслення _underscore . (вище краще) ! введіть тут опис зображень. До цих пір intersect_safe дав найкращі результати . ВИ і підкреслюєте найгірше.
neoswf

Додавання breakдо Simple js loopsзбільшення ППО / сек до ~ 10M
Richard

19
У разі , якщо ви пропустите його : найпростіший відповідь не загальноприйнятий один , але один в нижній частині : stackoverflow.com/questions/1885557 / ...
redben

Відповіді:


1077

Використовуйте комбінацію Array.prototype.filterта Array.prototype.indexOf:

array1.filter(value => -1 !== array2.indexOf(value))

Або, як запропонував в коментарях vrugtehagel , ви можете використовувати новішийArray.prototype.includes для ще більш простого коду:

array1.filter(value => array2.includes(value))

Для старих браузерів:

array1.filter(function(n) {
    return array2.indexOf(n) !== -1;
});

9
Що ви можете вирішити, додавши бібліотечну версію в прототип масиву.
Анон.

12
Так, але варто було згадати.
Тім Даун

18
Найкраща відповідь тут, як для простоти, так і для роботи з не числами
Muhd

41
intersection([1,2,1,1,3], [1])повертає [1, 1, 1]. Чи не повинен він повернутися просто [1]?
edjroot

21
Замість array2.indexOf(n) != -1одного можна також написати array2.includes(n)ще простіший код.
vrugtehagel

157

Руйнівний видається найпростішим, особливо якщо ми можемо припустити, що вхід сортований:

/* destructively finds the intersection of 
 * two arrays in a simple fashion.  
 *
 * PARAMS
 *  a - first array, must already be sorted
 *  b - second array, must already be sorted
 *
 * NOTES
 *  State of input arrays is undefined when
 *  the function returns.  They should be 
 *  (prolly) be dumped.
 *
 *  Should have O(n) operations, where n is 
 *    n = MIN(a.length, b.length)
 */
function intersection_destructive(a, b)
{
  var result = [];
  while( a.length > 0 && b.length > 0 )
  {  
     if      (a[0] < b[0] ){ a.shift(); }
     else if (a[0] > b[0] ){ b.shift(); }
     else /* they're equal */
     {
       result.push(a.shift());
       b.shift();
     }
  }

  return result;
}

Неруйнівне волосся повинно бути складнішим, оскільки ми повинні відстежувати показники:

/* finds the intersection of 
 * two arrays in a simple fashion.  
 *
 * PARAMS
 *  a - first array, must already be sorted
 *  b - second array, must already be sorted
 *
 * NOTES
 *
 *  Should have O(n) operations, where n is 
 *    n = MIN(a.length(), b.length())
 */
function intersect_safe(a, b)
{
  var ai=0, bi=0;
  var result = [];

  while( ai < a.length && bi < b.length )
  {
     if      (a[ai] < b[bi] ){ ai++; }
     else if (a[ai] > b[bi] ){ bi++; }
     else /* they're equal */
     {
       result.push(a[ai]);
       ai++;
       bi++;
     }
  }

  return result;
}

14
У численних помилках є intersect_safe: lengthвластивість в масивах, а не метод. Невизначена змінна iв result.push(a[i]);. Нарешті, це просто не працює в загальному випадку: два об'єкти, де жоден не більший, ніж інший за >оператором, не обов'язково рівні. intersect_safe( [ {} ], [ {} ] ), наприклад, дасть (як тільки виправлені раніше помилки) масив з одним елементом, що явно неправильно.
Тім Даун

1
@Tim Down: виправлено синтаксичні помилки, які ви вказали. Правильно чи некоректно вважати що-небудь ні загальним, ні меншим рівним, залежить від вимог. У початковому запитанні я нічого не помічаю, що вміст повинен містити масиви. Тепер, ви будете правильно сказати, що несподіване введення має оброблятися, але якщо специфікація вже трапилася, щоб диктувати, що введення повинно бути масивами чисел (як я припускав), код буде добре.
atk

1
@atk: Я вважаю, що в якості прикладу у запитанні використовуються масиви, які містять лише числа.
Тім Даун

4
Ви також можете використовувати .slice(0) можете створити клон масиву intersect_safe, а не відстежувати індекси.
johnluetke

1
@thesmart: ти маєш рацію, напевно є більш ефективні способи зробити це. Код, наведений вище, мав бути простим, а не швидким :)
atk

58

Якщо ваше середовище підтримує ECMAScript 6 Set , один простий і нібито ефективний спосіб (див. Посилання специфікації):

function intersect(a, b) {
  var setA = new Set(a);
  var setB = new Set(b);
  var intersection = new Set([...setA].filter(x => setB.has(x)));
  return Array.from(intersection);
}

Коротше, але менш читабельно (також без створення додаткового перехрестя Set):

function intersect(a, b) {
      return [...new Set(a)].filter(x => new Set(b).has(x));
}

Уникнути нове Setвід bкожного часу:

function intersect(a, b) {
      var setB = new Set(b);
      return [...new Set(a)].filter(x => setB.has(x));
}

Зауважте, що при використанні наборів ви отримуватимете лише окремі значення, таким чином, new Set[1,2,3,3].sizeоцінюйте до 3.


1
що це за [...setA]синтаксис? Якийсь особливий вид роботи javascript?
jxramos

1
@jxramos - це синтаксис Spread, і в цьому випадку він просто використовується для створення масиву з елементів набору. "Array.from (setA)" "також буде працювати в цьому випадку, але оскільки питання, яке було задано" найпростішим ", я намагався зробити його чистішим для читання в цьому рядку. З цього приводу я думаю, що код може бути простішим, тому я оновлю фрагмент.
nbarbosa

@nbarbosa Мені цікаво: чому ти "клонував" масив a? #filter не знищує початковий масив, правда? Це створює новий масив?
bplittle

@bplittle Я щойно створив масив із набору, щоб видалити дублікати, інакше безпосередньо використання масиву призведе до повернення дублікатів. Наприклад, якби я використовував масив безпосередньо, перетин ([1,2,2,4], [2,3]) отримав би [2, 2].
nbarbosa

2
Чи не x => new Set(b).has(x)перетворюється ця стрілка bв набір щоразу, коли вона виконується? Ви, ймовірно, повинні зберегти цей набір у змінній.
Аран-Фей

39

Використання Underscore.js або lodash.js

_.intersection( [0,345,324] , [1,0,324] )  // gives [0,324]

20
Оп попросив "без бібліотеки".
LinuxDisciple

@LinuxDisciple Моя помилка пропущення цього. Дякую за замітки
Сай Рам

33
У будь-якому випадку, це найкращий список google для цього пошуку, тому відповідь на бібліотеку корисна. Дякую.
webnoob

Я теж радий, що це було розміщено. Перший раз я відчув потребу в underscore.js. Зазвичай JavaScript-карта та скорочення трубопроводів виконують цю роботу елегантно, але не цього разу.
Шрідхар Сарнобат

Я <3 підкреслення і я <3 Джеремі Ашкенас (його творець), але навіть я настійно рекомендую замість цього перевірити Лодаша. Це в основному вища версія Underscore (вона спочатку була виделкою), єдиним недоліком якої є оптимізований (і, таким чином, майже нечитабельний) вихідний код. Люди, що підкреслювали, навіть розглядали можливість позбутися підкреслення повністю (і просто казали людям користуватися Лодашем), але люди, котрі піклуються про читабельність вихідного коду, стверджували, що це збережеться (я фактично був на цій стороні, але з тих пір я перейшов у Лодаш). @see github.com/jashkenas/underscore/issues/2182
machineghost

14

Мій внесок в ES6. Загалом, він знаходить перетин масиву з невизначеною кількістю масивів, поданих у якості аргументів.

Array.prototype.intersect = function(...a) {
  return [this,...a].reduce((p,c) => p.filter(e => c.includes(e)));
}
var arrs = [[0,2,4,6,8],[4,5,6,7],[4,6]],
     arr = [0,1,2,3,4,5,6,7,8,9];

document.write("<pre>" + JSON.stringify(arr.intersect(...arrs)) + "</pre>");


цей код виглядає чудово, але я не розумію його повністю. Можна пояснити це будь ласка?
звичайний

1
@novembersky Він збирає всі предметні масиви в масив, як [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7],[4,6]]і потім застосовується .reduce(). Перша [0,1,2,3,4,5,6,7,8,9].filter( e => [0,2,4,6,8].includes(e)операція виконується , і результат стає новим pі cстає [4,5,6,7]в наступному повороті і продовжує так далі вгору до тих пір, поки більше не cзалишилося.
Скоротити

1
Це дороге рішення, якщо ви працюєте з великими наборами даних.
Madbreaks

1
Не змінюйте prototypeзайве.
fregante

14

// Return elements of array a that are also in b in linear time:
function intersect(a, b) {
  return a.filter(Set.prototype.has, new Set(b));
}

// Example:
console.log(intersect([1,2,3], [2,3,4,5]));

Я рекомендую вище лаконічне рішення, яке перевершує інші реалізації на великих ресурсах. Якщо продуктивність на невеликих входах має значення, перевірте наведені нижче варіанти.

Альтернативи та порівняння ефективності:

Дивіться нижченаведений фрагмент альтернативних реалізацій та перевірте https://jsperf.com/array-intersection-compitation для порівняння продуктивності.

Результати в Firefox 53:

  • Оп / сек на великих масивах (10000 елементів):

    filter + has (this)               523 (this answer)
    for + has                         482
    for-loop + in                     279
    filter + in                       242
    for-loops                          24
    filter + includes                  14
    filter + indexOf                   10
  • Оп / сек на малих масивах (100 елементів):

    for-loop + in                 384,426
    filter + in                   192,066
    for-loops                     159,137
    filter + includes             104,068
    filter + indexOf               71,598
    filter + has (this)            43,531 (this answer)
    filter + has (arrow function)  35,588

2
intersect([1,2,2,3], [2,3,4,5])повертає [2, 2, 3].
SeregPie

1
@SeregPie Рівно. Відповідно до коментаря "Повернення елементів масиву a, які також є в b"
le_m

Якісна відповідь, однак використання Sets принципово змінює результати, оскільки питання ОП задається лише про перехрестя масивів і не згадує / не передбачає, як обробляти дублікати. Соромлячись цього, ця відповідь може дати несподівані результати, коли існують дублікати.
Madbreaks

1
Любіть це, але ви додали непотрібну функцію з "фільтр + включає". спробуйте a.filter(b.includes). Він повинен працювати значно швидше (те саме, що оновлення функції).
SEOF

11

Як щодо просто використання асоціативних масивів?

function intersect(a, b) {
    var d1 = {};
    var d2 = {};
    var results = [];
    for (var i = 0; i < a.length; i++) {
        d1[a[i]] = true;
    }
    for (var j = 0; j < b.length; j++) {
        d2[b[j]] = true;
    }
    for (var k in d1) {
        if (d2[k]) 
            results.push(k);
    }
    return results;
}

редагувати:

// new version
function intersect(a, b) {
    var d = {};
    var results = [];
    for (var i = 0; i < b.length; i++) {
        d[b[i]] = true;
    }
    for (var j = 0; j < a.length; j++) {
        if (d[a[j]]) 
            results.push(a[j]);
    }
    return results;
}

1
Це є лише шансом, якщо ваші масиви містять лише рядки чи цифри, і якщо жоден із сценаріїв на вашій сторінці не змішився Object.prototype.
Тім Даун

2
Прикладом ОП було використання чисел, і якщо сценарій переплутався з Object.prototype, сценарій слід переписати або видалити.
Стівен Ювіг

Вам не потрібно і (d1), і (d2). Створіть (d2), потім проведіть цикл через (a) замість циклу (d1).
StanleyH

Повинен бути d[b[i]] = true;замість d[b[j]] = true;( iне j). Але для редагування потрібно 6 символів.
Іжакі

@Izhaki спасибі, виправлено. (Додано // коментар, щоб обійти мінімальну вимогу редагування.)
Стівен Ювіг

8
  1. Сортуйте його
  2. перевіряйте по черзі з індексу 0, створюйте з цього новий масив.

Щось подібне, Хоча не перевірено добре.

function intersection(x,y){
 x.sort();y.sort();
 var i=j=0;ret=[];
 while(i<x.length && j<y.length){
  if(x[i]<y[j])i++;
  else if(y[j]<x[i])j++;
  else {
   ret.push(x[i]);
   i++,j++;
  }
 }
 return ret;
}

alert(intersection([1,2,3], [2,3,4,5]));

PS: Алгоритм, призначений лише для Numbers and Normal Strings, перетин довільних масивів об'єктів може не працювати.


3
Сортування не обов’язково допоможе для масивів довільних об’єктів
Тім Даун

Якщо масив не відсортований, потрібно перевести цикл близько 1 000 000 разів, коли ви перетинаєте масив довжиною 1000 х масив довжиною 1000
ВАС

Я думаю, ви пропустили мою думку, що полягає в тому, що довільні об’єкти в JavaScript не мають природного порядку сортування, тобто сортування масиву довільних об'єктів не призведе до групування рівних об'єктів. Непогано мати ефективний алгоритм, який не працює.
Тім Даун

Ах вибачте, я пропустив "довільні об'єкти", так, ви маєте рацію. ці об'єкти не можуть його сортувати, і алгоритм може не працювати на них.
ВИ

8

Продуктивність реалізації @ atk для відсортованих масивів примітивів можна покращити за допомогою .pop, а не .shift.

function intersect(array1, array2) {
   var result = [];
   // Don't destroy the original arrays
   var a = array1.slice(0);
   var b = array2.slice(0);
   var aLast = a.length - 1;
   var bLast = b.length - 1;
   while (aLast >= 0 && bLast >= 0) {
      if (a[aLast] > b[bLast] ) {
         a.pop();
         aLast--;
      } else if (a[aLast] < b[bLast] ){
         b.pop();
         bLast--;
      } else /* they're equal */ {
         result.push(a.pop());
         b.pop();
         aLast--;
         bLast--;
      }
   }
   return result;
}

Я створив орієнтир за допомогою jsPerf: http://bit.ly/P9FrZK . Використовувати .pop приблизно втричі швидше.


1
Так само як бічна нота для інших - це буде працювати лише для чисел, а не для рядків.
Іжакі

Зауважте, що якщо ви заміните a[aLast] > b[bLast]на a[aLast].localeCompare(b[bLast]) > 0(і те ж саме else ifнижче), це буде працювати над рядками.
andrew

1
Різниця швидкостей залежить від розміру масивів, оскільки .popO (1) і .shift()O (n)
Esailija

8

Використання jQuery :

var a = [1,2,3];
var b = [2,3,4,5];
var c = $(b).not($(b).not(a));
alert(c);

8
Це також можна записати як c = $(b).filter(a);, але я б не рекомендував покладатися на jQuery для такого роду маніпулювання масивом, оскільки в документації згадується лише те, що він працює для елементів.
Стринер

1
Це не відповідає на запитання ОП: "Що найпростіше, без бібліотечного коду ..."
Madbreaks

7

Для масивів, що містять лише рядки або числа, ви можете зробити щось із сортуванням, як і в деяких інших відповідях. Що стосується загального випадку масивів довільних об'єктів, я не думаю, що ви можете уникнути цього довго. Далі наведено перетин будь-якої кількості масивів, поданих як параметри для arrayIntersection:

var arrayContains = Array.prototype.indexOf ?
    function(arr, val) {
        return arr.indexOf(val) > -1;
    } :
    function(arr, val) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === val) {
                return true;
            }
        }
        return false;
    };

function arrayIntersection() {
    var val, arrayCount, firstArray, i, j, intersection = [], missing;
    var arrays = Array.prototype.slice.call(arguments); // Convert arguments into a real array

    // Search for common values
    firstArray = arrays.pop();
    if (firstArray) {
        j = firstArray.length;
        arrayCount = arrays.length;
        while (j--) {
            val = firstArray[j];
            missing = false;

            // Check val is present in each remaining array 
            i = arrayCount;
            while (!missing && i--) {
                if ( !arrayContains(arrays[i], val) ) {
                    missing = true;
                }
            }
            if (!missing) {
                intersection.push(val);
            }
        }
    }
    return intersection;
}

arrayIntersection( [1, 2, 3, "a"], [1, "a", 2], ["a", 1] ); // Gives [1, "a"]; 

Це працює лише в тому випадку, коли ідентичність об'єкта є єдиною формою рівності.
Стівен Гувіг

Ну так, але я думаю, що саме це здавалося б природним для більшості людей. Також тривіально підключити альтернативну функцію, щоб виконати інший тест рівності.
Тім Даун

Я думаю, ви випадково створюєте глобальну змінну firstArr у своєму прикладі.
Джейсон Джексон

@JasonJackson: Ти маєш рацію, дякую. Ясно передумав, чи потрібно викликати змінну firstArrчи firstArrayне оновив усі посилання. Виправлено.
Tim Down

7

Це досить коротко за допомогою ES2015 та Sets. Приймає такі масиви, як String і видаляє дублікати.

let intersection = function(a, b) {
  a = new Set(a), b = new Set(b);
  return [...a].filter(v => b.has(v));
};

console.log(intersection([1,2,1,2,3], [2,3,5,4,5,3]));

console.log(intersection('ccaabbab', 'addb').join(''));


Перетворити з Set to array з [... a] видалить повторювані елементи, гарна ідея, спасибі
V-SHY

1
Це рішення було надано двічі до вашого.
Madbreaks

7

Ви могли б використовувати в Setякості thisArgз Array#filterі взяти в Set#hasякості зворотного виклику.

function intersection(a, b) {
    return a.filter(Set.prototype.has, new Set(b));
}

console.log(intersection([1, 2, 3], [2, 3, 4, 5]));


Я не знаю, чому це не набрало більше голосів. Це однозначно найкраща відповідь.
Пол Руні

5

Крихітний перехід до найменшого тут (рішення filter / indexOf ), а саме створення індексу значень в одному з масивів за допомогою об’єкта JavaScript, зменшить його з O (N * M) до "ймовірно" лінійного часу. джерело1 джерело2

function intersect(a, b) {
  var aa = {};
  a.forEach(function(v) { aa[v]=1; });
  return b.filter(function(v) { return v in aa; });
}

Це не найпростіше рішення (це більше коду, ніж фільтр + indexOf ), а також не найшвидше (можливо, повільніше за постійним фактором, ніж intersect_safe () ), але здається досить гарним балансом. Це дуже просто, при цьому забезпечується хороша продуктивність, і він не вимагає попередньо відсортованих входів.


5

Інший індексований підхід, здатний опрацювати будь-яку кількість масивів одночасно:

// Calculate intersection of multiple array or object values.
function intersect (arrList) {
    var arrLength = Object.keys(arrList).length;
        // (Also accepts regular objects as input)
    var index = {};
    for (var i in arrList) {
        for (var j in arrList[i]) {
            var v = arrList[i][j];
            if (index[v] === undefined) index[v] = 0;
            index[v]++;
        };
    };
    var retv = [];
    for (var i in index) {
        if (index[i] == arrLength) retv.push(i);
    };
    return retv;
};

Він працює лише для значень, які можна оцінити як рядки, і ви повинні передавати їх як масив на зразок:

intersect ([arr1, arr2, arr3...]);

... але він прозоро приймає об'єкти як параметр або як будь-який з елементів, що перетинаються (завжди повертаючи масив загальних значень). Приклади:

intersect ({foo: [1, 2, 3, 4], bar: {a: 2, j:4}}); // [2, 4]
intersect ([{x: "hello", y: "world"}, ["hello", "user"]]); // ["hello"]

Редагувати: Я щойно помітив, що це, певним чином, трохи баггі.

Тобто: Я кодував це, думаючи, що вхідні масиви не можуть самі містити повторів (як це не наведено в прикладі).

Але якщо вхідні масиви містять повтори, це призведе до неправильних результатів. Приклад (використовуючи нижче реалізацію):

intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]);
// Expected: [ '1' ]
// Actual: [ '1', '3' ]

На щастя, це легко виправити, просто додавши індексацію другого рівня. Це є:

Змінити:

        if (index[v] === undefined) index[v] = 0;
        index[v]++;

автор:

        if (index[v] === undefined) index[v] = {};
        index[v][i] = true; // Mark as present in i input.

... і:

         if (index[i] == arrLength) retv.push(i);

автор:

         if (Object.keys(index[i]).length == arrLength) retv.push(i);

Повний приклад:

// Calculate intersection of multiple array or object values.
function intersect (arrList) {
    var arrLength = Object.keys(arrList).length;
        // (Also accepts regular objects as input)
    var index = {};
    for (var i in arrList) {
        for (var j in arrList[i]) {
            var v = arrList[i][j];
            if (index[v] === undefined) index[v] = {};
            index[v][i] = true; // Mark as present in i input.
        };
    };
    var retv = [];
    for (var i in index) {
        if (Object.keys(index[i]).length == arrLength) retv.push(i);
    };
    return retv;
};

intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]); // [ '1' ]

2
Це далеко не найкраща відповідь з невеликою модифікацією. Після var v = додавання рядка if (typeof v == 'function') continue;він пропустить додавання функцій до результатів. Дякую!
Zsolti

Дякую @Zsolti. Я не додаю вашої пропозиції, оскільки функціонування (і спосіб, яким ми хочемо це впоратися) виходить за рамки початкового питання. Але дивіться мою редакцію: Якщо у вас можуть бути повтори у вхідних масивах, то оригінальна реалізація є помилковою. Я виправив це у своїй редакції. ... З іншого боку, якщо ви точно знаєте, що повторення не буде, оригінальна реалізація трохи дешевша.
бутифе

... щодо функцій їх також можна перетинати: Якщо ми виявимо їх, як каже @Zsolti (з if (typeof v == 'function'), тоді ми можемо використовувати його стрифікацію ( v.toString()) в якості ключа для індексу. Але нам потрібно зробити щось, щоб зберегти його неушкодженим. Найпростіший спосіб зробити це просто призначити оригінальну функцію як значення замість простого булевого справжнього значення. Але, у цьому випадку, останній deindexaton також повинен бути змінений для виявлення цієї умови та відновлення потрібного значення (функції).
bitifet

Наскільки швидко це може бути з 30 масивами зі 100 елементами .. як використовується процесор?
aidonsnous

5

Ви можете використовувати (для всіх браузерів, крім IE):

const intersection = array1.filter(element => array2.includes(element));

або для IE:

const intersection = array1.filter(element => array2.indexOf(element) !== -1);

Було б добре, якби ви могли перетворити це на функцію
lalanche1

@ avalanche1 const intersection = (a1, a2) => a1.filter (e => a2.includes (e));
jota3

4
function intersection(A,B){
var result = new Array();
for (i=0; i<A.length; i++) {
    for (j=0; j<B.length; j++) {
        if (A[i] == B[j] && $.inArray(A[i],result) == -1) {
            result.push(A[i]);
        }
    }
}
return result;
}

4

З деякими обмеженнями на ваші дані ви можете це зробити лінійно час!

Для натуральних чисел : використовуйте масив, який відображає значення булевого типу "бачив / не бачив".

function intersectIntegers(array1,array2) { 
   var seen=[],
       result=[];
   for (var i = 0; i < array1.length; i++) {
     seen[array1[i]] = true;
   }
   for (var i = 0; i < array2.length; i++) {
     if ( seen[array2[i]])
        result.push(array2[i]);
   }
   return result;
}

Існує подібна техніка для предметів : візьміть фіктивний ключ, встановіть його на "true" для кожного елемента масиву1, а потім шукайте цей ключ в елементах масиву2. Прибирайте, коли закінчите.

function intersectObjects(array1,array2) { 
   var result=[];
   var key="tmpKey_intersect"
   for (var i = 0; i < array1.length; i++) {
     array1[i][key] = true;
   }
   for (var i = 0; i < array2.length; i++) {
     if (array2[i][key])
        result.push(array2[i]);
   }
   for (var i = 0; i < array1.length; i++) {
     delete array1[i][key];
   }
   return result;
}

Звичайно, ви повинні бути впевнені, що ключ не з’явився раніше, інакше ви знищите свої дані ...


До речі, це можна легко розширити, щоб перетинати будь-яку кількість масивів: замінюйте булеві числа на цілі числа та збільшуючи щоразу, коли це буде видно: ви можете легко прочитати перетин на останньому турі.
tarulen

Цікаве рішення, мені це подобається. Більшість інших рішень є O (n ^ 2), але це O (n). Я додав цілий код до скрипки про ефективність ericP тут jsfiddle.net/321juyLu/2 . Прийшов 3-ий, мені подобається :)
rmcsharry

3

Я буду сприяти тому, що для мене найкраще працює:

if (!Array.prototype.intersect){
Array.prototype.intersect = function (arr1) {

    var r = [], o = {}, l = this.length, i, v;
    for (i = 0; i < l; i++) {
        o[this[i]] = true;
    }
    l = arr1.length;
    for (i = 0; i < l; i++) {
        v = arr1[i];
        if (v in o) {
            r.push(v);
        }
    }
    return r;
};
}


2

'use strict'

// Example 1
function intersection(a1, a2) {
    return a1.filter(x => a2.indexOf(x) > -1)
}

// Example 2 (prototype function)
Array.prototype.intersection = function(arr) {
    return this.filter(x => arr.indexOf(x) > -1)
} 

const a1 = [1, 2, 3]
const a2 = [2, 3, 4, 5]

console.log(intersection(a1, a2))
console.log(a1.intersection(a2))


2

Функціональний підхід з ES2015

Функціональний підхід повинен розглянути можливість використання лише чистих функцій без побічних ефектів, кожна з яких стосується лише одного завдання.

Ці обмеження підвищують сумісність та повторне використання функцій, що займаються.

// small, reusable auxiliary functions

const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const apply = f => x => f(x);


// intersection

const intersect = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? true
     : false
  ) (xs);
};


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// run it

console.log( intersect(xs) (ys) );

Зверніть увагу, що використовується нативний Setтип, який має вигідні показники пошуку.

Уникайте дублікати

Очевидно, що неодноразово зустрічаються предмети з першого Arrayзберігаються, а друге Arrayдедублюється. Це може бути чи не бажана поведінка. Якщо вам потрібен унікальний результат, просто зверніться dedupeдо першого аргументу:

// auxiliary functions

const apply = f => x => f(x);
const comp = f => g => x => f(g(x));
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// intersection

const intersect = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? true
     : false
  ) (xs);
};


// de-duplication

const dedupe = comp(afrom) (createSet);


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// unique result

console.log( intersect(dedupe(xs)) (ys) );

Обчисліть перетин будь-якої кількості Arrays

Якщо ви хочете , щоб обчислити перетин як завгодно числа Arrayз просто компонувати intersectз foldl. Ось функція зручності:

// auxiliary functions

const apply = f => x => f(x);
const uncurry = f => (x, y) => f(x) (y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);


// intersection

const intersect = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? true
     : false
  ) (xs);
};


// intersection of an arbitrarily number of Arrays

const intersectn = (head, ...tail) => foldl(intersect) (head) (tail);


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];
const zs = [0,1,2,3,4,5,6];


// run

console.log( intersectn(xs, ys, zs) );


Вражаючий функціонал: довелося зробити подвійний процес, щоб підтвердити, що це не Haskell. Єдина нітпік: (expr ? true : false)зайва. Використовуйте, лише exprякщо фактичні булеви не потрібні, просто truthy / falesy.
jose_castro_arnaud

2

Для простоти:

// Usage
const intersection = allLists
  .reduce(intersect, allValues)
  .reduce(removeDuplicates, []);


// Implementation
const intersect = (intersection, list) =>
  intersection.filter(item =>
    list.some(x => x === item));

const removeDuplicates = (uniques, item) =>
  uniques.includes(item) ? uniques : uniques.concat(item);


// Example Data
const somePeople = [bob, doug, jill];
const otherPeople = [sarah, bob, jill];
const morePeople = [jack, jill];

const allPeople = [...somePeople, ...otherPeople, ...morePeople];
const allGroups = [somePeople, otherPeople, morePeople];

// Example Usage
const intersection = allGroups
  .reduce(intersect, allPeople)
  .reduce(removeDuplicates, []);

intersection; // [jill]

Переваги:

  • бруд просто
  • орієнтований на дані
  • працює для довільної кількості списків
  • працює для довільної довжини списків
  • працює для довільних типів значень
  • працює для довільного порядку сортування
  • зберігає форму (порядок першої появи в будь-якому масиві)
  • виходить рано, де це можливо
  • безпечна пам’ять, не має підробки прототипів функцій / масиву

Недоліки:

  • більше використання пам'яті
  • більше використання процесора
  • вимагає розуміння скорочення
  • вимагає розуміння потоку даних

Ви б не хотіли використовувати це для роботи з 3D-двигуном або ядром, але якщо у вас виникли проблеми з запуском цього додатка на основі подій, ваш дизайн має більші проблеми.


2

.reduceскласти карту і .filterзнайти перехрестя. deleteвсередині .filterдозволяє нам ставитися до другого масиву як до унікального набору.

function intersection (a, b) {
  var seen = a.reduce(function (h, k) {
    h[k] = true;
    return h;
  }, {});

  return b.filter(function (k) {
    var exists = seen[k];
    delete seen[k];
    return exists;
  });
}

Мені здається, що цей підхід досить легко міркувати. Це виконує в постійному часі.


2

Це, мабуть, найпростіший, крім list1.filter (n => list2.includes (n))

var list1 = ['bread', 'ice cream', 'cereals', 'strawberry', 'chocolate']
var list2 = ['bread', 'cherry', 'ice cream', 'oats']

function check_common(list1, list2){
	
	list3 = []
	for (let i=0; i<list1.length; i++){
		
		for (let j=0; j<list2.length; j++){	
			if (list1[i] === list2[j]){
				list3.push(list1[i]);				
			}		
		}
		
	}
	return list3
	
}

check_common(list1, list2) // ["bread", "ice cream"]


це має складну часову складність O (nm) ... це можна було б вирішити в O (n + m)
alchuang

2

Якщо вам потрібно, щоб він мав обробляти пересічні масиви:

const intersect = (a, b, ...rest) => {
  if (rest.length === 0) return [...new Set(a)].filter(x => new Set(b).has(x));
  return intersect(a, intersect(b, ...rest));
};

console.log(intersect([1,2,3,4,5], [1,2], [1, 2, 3,4,5], [2, 10, 1])) // [1,2]


Але наскільки швидко це рішення для 30 масивів зі 100 елементами?
допоміжний

Це не використовує нічого, окрім методів Javascript, і для цього VM, в якій запускається код, вільний для оптимізації, наскільки це можливо. Я повністю впевнений, що не існує більш швидкого рішення, враховуючи, що ви працюєте в сучасній версії V8 щодо віку цього коментаря.
Белфордз

2

Простий спосіб у стилі ES6.

const intersection = (a, b) => {
  const s = new Set(b);
  return a.filter(x => s.has(x));
};

Приклад:

intersection([1, 2, 3], [4, 3, 2]); // [2, 3]

2

Я написав функцію інтесекції, яка навіть може виявити перетин масиву об'єктів на основі певної властивості цих об'єктів.

Наприклад,

if arr1 = [{id: 10}, {id: 20}]
and arr2 =  [{id: 20}, {id: 25}]

і ми хочемо перетин на основі idвластивості, тоді вихід повинен бути:

[{id: 20}]

Таким чином, функція для того ж (примітка: код ES6):

const intersect = (arr1, arr2, accessors = [v => v, v => v]) => {
    const [fn1, fn2] = accessors;
    const set = new Set(arr2.map(v => fn2(v)));
    return arr1.filter(value => set.has(fn1(value)));
};

і ви можете викликати функцію так:

intersect(arr1, arr2, [elem => elem.id, elem => elem.id])

Також зверніть увагу: ця функція знаходить перетин, враховуючи, що перший масив є основним масивом, і таким чином результат перетину буде результатом первинного масиву.


2

Подумайте, що це буде швидше, якщо час (O array1 + array2) припустимо, що map.has () - ~ O (1). Якщо помиляєтесь, виправте мене.

const intersection = (a1, a2) => {
  let map = new Map();
  let result = []
  for (let i of a1) {
    if (!map.has(i)) map.set(i, true);
  }
  for (let i of a2) {
    if (map.has(i)) result.push(i)
  }
  return result;
}


1

Ось реалізація underscore.js :

_.intersection = function(array) {
  if (array == null) return [];
  var result = [];
  var argsLength = arguments.length;
  for (var i = 0, length = array.length; i < length; i++) {
    var item = array[i];
    if (_.contains(result, item)) continue;
    for (var j = 1; j < argsLength; j++) {
      if (!_.contains(arguments[j], item)) break;
    }
    if (j === argsLength) result.push(item);
  }
  return result;
};

Джерело: http://underscorejs.org/docs/underscore.html#section-62


Непогана довідка, якщо доступна
нерегулярна оцінка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.