webentwicklung-frage-antwort-db.com.de

Übereinstimmungen zwischen mehreren JavaScript-Arrays suchen

Ich habe mehrere Arrays mit String-Werten und möchte diese miteinander vergleichen und nur die übereinstimmenden Ergebnisse beibehalten, die zwischenALLvon ihnen identisch sind.

In Anbetracht dieses Beispielcodes:

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

Ich möchte das folgende Array erstellen, das Übereinstimmungen aus allen angegebenen Arrays enthält:

['Apple', 'fish', 'pizza']

Ich weiß, dass ich alle Arrays mit var newArr = arr1.concat(arr2, arr3); kombinieren kann, aber das gibt mir nur ein Array mit allem plus den Duplikaten. Kann dies leicht gemacht werden, ohne dass der Aufwand für Bibliotheken wie underscore.js erforderlich ist?

(Großartig, und jetzt Ich habe auch Hunger!)

EDITIch sollte erwähnen, dass es eine unbekannte Anzahl von Arrays geben könnte, ich habe nur 3 als Beispiel verwendet.

50
Chris Barr
var result = arrays.shift().filter(function(v) {
    return arrays.every(function(a) {
        return a.indexOf(v) !== -1;
    });
});

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

Sie können zuerst das äußere Array sortieren, um das kürzeste Array am Anfang zu erhalten ...

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

Der Vollständigkeit halber sei hier eine Lösung angegeben, die Duplikate in den Arrays behandelt. Es verwendet .reduce() anstelle von .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/

80
user1106925

Nun, da Sie der Frage eine unbestimmte Anzahl von Arrays hinzugefügt haben, wird hier ein weiterer Ansatz verwendet, bei dem die Anzahl der einzelnen Elemente in einem Objekt erfasst und dann die Elemente mit der maximalen Anzahl sortiert werden.

Vorteile dieses Ansatzes:

  1. ~ 15x schneller als Brute-Force-Suchoptionen (von anderen Antworten verwendet), wenn Arrays größer sind
  2. Benötigt kein ES5- oder ES5-Shim (funktioniert mit allen Browsern)
  3. Vollständig zerstörungsfrei (ändert keine Quelldaten)
  4. Verarbeitet doppelte Elemente in Quell-Arrays
  5. Verarbeitet eine beliebige Anzahl von Eingabearrays

Und hier ist der Code:

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);
}    

Arbeitsdemo: http://jsfiddle.net/jfriend00/52mAP/

Zu Ihrer Information, dies erfordert kein ES5 und funktioniert daher in allen Browsern ohne Shim.

In einem Leistungstest für 15 Arrays mit einer Länge von je 1000 war dies mehr als zehnmal schneller als die Suchmethode, die in dieser Jsperf-Antwort verwendet wurde: http://jsperf.com/in-all-arrays .


Hier ist eine Version, die eine ES6 Map und Set verwendet, um die Anzahl zu bestimmen und zu verfolgen. Dies hat den Vorteil, dass der Datentyp erhalten bleibt und beliebig sein kann (er muss nicht einmal eine natürliche String-Konvertierung haben. Die Daten können sogar Objekte sein, obwohl Objekte verglichen werden, weil sie genau das gleiche Objekt sind und nicht das gleiche haben Eigenschaften/Werte).

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>";

12
jfriend00

Angenommen, es gibt ein Array von Arrays, von denen wir den Schnittpunkt finden möchten, könnte der einfachste Ansatz für einen einzelnen Liner sein

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>");

9
Redu

Ein paar Gedanken - Sie können nur die Elemente im kürzesten Array Vergleichen und Duplikate im zurückgegebenen Array vermeiden.

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();

rückgabewert: Apple,fish,pizza

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

3
kennebec

Hier geht es um eine einzeilige Lösung. Sie können es in zwei Denkschritte aufteilen:

  1. Berechnen Sie den Join/Schnittpunkt zwischen zwei 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}`);

  1. Reduzieren Sie den Inhalt: Berechnen Sie den Schnittpunkt zwischen dem akkumulierten Schnittpunkt und dem nächsten 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}`);

2
dinigo

Array von Arrays annehmen und alle Arrays durchsehen:

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;
})
2
charlietfl

Dies sollte für eine beliebige Anzahl von Arrays funktionieren:

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);
1
Blender

Dies ist im Wesentlichen eine Zusammenstellung aller niedergeschlagenen Antworten:

	 // 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>

0
bob

Nur um das Ganze zu verdeutlichen, ein weiterer Ansatz mit langen Händen:

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']
];

Bearbeiten

Funktion zum Auffinden nie aufzuzählender Eigenschaften basierend auf diesem Objekt in 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>'));
0
RobG