Ceea ce este cel mai concis și eficient mod de a afla dacă o matrice JavaScript conține o valoare?
Acesta este singurul mod în care știu să o facă:
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
Există o mai bună și mai concis mod de a realiza acest lucru?
Acest lucru este foarte strâns legată de Stack Overflow întrebare cel Mai bun mod de a găsi un element într-o Matrice JavaScript? care vizează găsirea de obiecte într-o matrice folosind indexOf
.
Browserele actuale au Matrice#include
, care nu exact ca, este larg acceptat, și are o polyfill pentru browsere mai vechi.
> ['joe', 'jane', 'mary'].includes('jane');
true
Puteți folosi, de asemenea, Matrice#indexOf
, care este mai puțin directe, dar nu't nevoie de Polyfills din data de browsere.
jQuery oferă $.inArray
, care este funcțional echivalent cu Matrice#indexOf
.
underscore.js, un JavaScript utility library, oferte _.conține(lista, valoare)
, alias _.include(lista, valoare)
, ambele din care utiliza indexOf pe plan intern, dacă a trecut o matrice JavaScript.
Unele alte cadre oferta metode similare:
dojo.indexOf(matrice, valoarea, [fromIndex, findLast])
matrice.indexOf(valoare)
matrice.indexOf(valoare)
findValue(matrice, valoare)
matrice.indexOf(valoare)
Ext.Matrice.conține(matrice, valoare)
_.include(matrice, valoarea, [de])
(este `_.conține prealabilă 4.0.0)R. include(valoare, matrice)
matrice.include(valoare)
Observați că unele cadre pună în aplicare acest lucru ca pe o funcție, în timp ce alții adaugă funcția de a matrice prototip.
Actualizare începând din 2019: Acest răspuns este din 2008 (11 ani!) și nu este relevantă pentru modern JS utilizare. Promis de îmbunătățire a performanței s-a bazat pe un punct de referință făcut în browsere de acel moment. S-ar putea să nu fie relevante pentru modern JS execuție contexte. Dacă aveți nevoie de o soluție simplă, uita-te pentru alte răspunsuri. Dacă aveți nevoie de cea mai bună performanță, de referință pentru tine relevante executarea medii.
Cum au spus și alții, repetare prin matrice este, probabil, cel mai bun mod, dar a fost dovedit că o scădere in timp ce bucla este cel mai rapid mod de a repeta în JavaScript. Deci, poate doriți să rescrie codul, după cum urmează:
function contains(a, obj) {
var i = a.length;
while (i--) {
if (a[i] === obj) {
return true;
}
}
return false;
}
Desigur, ar fi bine să se extindă Gama de prototip:
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
Și acum puteți folosi pur și simplu următoarele:
alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false
indexOf
poate, dar's, un "JavaScript extensie a ECMA-262 standard; ca atare, acesta nu poate fi prezent în alte implementări ale standardului."
Exemplu:
[1, 2, 3].indexOf(1) => 0
["foo", "bar", "baz"].indexOf("bar") => 1
[1, 2, 3].indexOf(4) => -1
AFAICS Microsoft nu nu oferă un fel de alternativă pentru acest lucru, dar puteți adăuga funcționalitate similară pentru tablouri în Internet Explorer (și alte browsere care nu't suport indexOf
) dacă doriți să, ca căutare rapidă Google dezvăluie (de exemplu, acesta).
ECMAScript 7 introduce Matrice.prototip.include
.
Acesta poate fi folosit astfel:
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
Se acceptă, de asemenea, opțional, un al doilea argument fromIndex
:
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
Spre deosebire de indexOf
, care utilizează Strictă Egalitate de Comparare, "include" se compară utilizând SameValueZero egalitatea algoritm. Asta înseamnă că puteți detecta dacă o matrice include un "NaN":
[1, 2, NaN].includes(NaN); // true
De asemenea, spre deosebire de indexOf
, "include" nu sări peste lipsesc indici:
new Array(5).includes(undefined); // true
În prezent, acesta's încă un proiect, dar pot fi polyfilled pentru a face să funcționeze pe toate browserele.
Primele răspunsuri presupun tipuri primitive, dar dacă doriți pentru a afla dacă o matrice conține un ** obiect cu unele trăsături, Matrice.prototip.unele() este o soluție elegantă:
const items = [ {a: '1'}, {a: '2'}, {a: '3'} ]
items.some(item => item.a === '3') // returns true
items.some(item => item.a === '4') // returns false
Cel mai frumos lucru despre el este că repetare este anulat odată ce elementul este găsit atât de inutile repetare a ciclurilor de-a cruțat.
De asemenea, se potrivește foarte bine într-un "dacă" declarația de când se întoarce un boolean:
if (items.some(item => item.a === '3')) {
// do something
}
* Ca jamess subliniat în comentariu, la momentul de acest răspuns, septembrie 2018, Matrice.prototip.unele()
este pe deplin susținută: caniuse.com masa suport
Aici's a JavaScript 1.6 compatibile implementarea de Matrice.indexOf`:
if (!Array.indexOf) {
Array.indexOf = [].indexOf ?
function(arr, obj, from) {
return arr.indexOf(obj, from);
} :
function(arr, obj, from) { // (for IE6)
var l = arr.length,
i = from ? parseInt((1 * from) + (from < 0 ? l : 0), 10) : 0;
i = i < 0 ? 0 : i;
for (; i < l; i++) {
if (i in arr && arr[i] === obj) {
return i;
}
}
return -1;
};
}
Extinderea JavaScript "Matrice" obiectul este o idee foarte proastă pentru că introduce noi proprietăți (personalizat metode de) in de în
bucle de care se pot sparge existente script-uri. Cu câțiva ani în urmă, autorii Prototip biblioteca a trebuit să re-inginer biblioteca lor în aplicare pentru a elimina doar acest tip de lucru.
Dacă tu nu't nevoie să vă faceți griji despre compatibilitatea cu alte JavaScript care rulează pe pagina ta, du-te pentru ea, în caz contrar, am'd recomanda mai ciudat, dar mai sigure liber-în picioare funcție de soluție.
Gândire afară de la cutie pentru o secundă, dacă se face acest apel de multe ori, este mult mai eficient să se folosească <grevă>un tablou asociativ o Hartă pentru a face căutările folosind o funcție hash.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
O, sperăm, mai repede bidirecțională indexOf
/ `lastIndexOf alternativă
În timp ce nouă metodă cuprinde este foarte frumos, suportul este practic zero pentru acum.
L's de mult timp că am fost de gândire de modul de a înlocui lent indexOf/lastIndexOf funcții.
O performant a fost deja găsit, se uită la partea de sus răspunsuri. De aceia am ales "conține" funcția postat de @Damir Zekic, care ar trebui să fie cel mai rapid. Dar, de asemenea, prevede că indicii de referință sunt din 2008 și așa sunt depășite.
Prefer, de asemenea, in timp ce peste "pentru", dar nu pentru un motiv anume pentru care am încheiat scris funcționa cu o buclă. Ar putea fi, de asemenea, terminat cu un timp...`.
Am fost curios dacă iterație a fost mult mai lent dacă am verifica ambele părți de matrice în timp ce o faci. Se pare că nu, și atunci această funcție este de aproximativ două ori mai repede decât partea de sus a votat altele. Evident, l's, de asemenea, mai repede decât cea natală. Acest lucru într-un mediu din lumea reală, în cazul în care nu știi niciodată dacă valoarea sunteți în căutarea este la începutul sau la sfârșitul șirului.
Când știi că ai împins-o matrice cu o valoare, folosind lastIndexOf rămâne, probabil, cea mai bună soluție, dar dacă trebuie să călătorească prin mari tablouri, iar rezultatul ar putea fi peste tot, acest lucru ar putea fi o soluție solidă pentru a face lucrurile mai repede.
Bidirecțională indexOf/lastIndexOf
function bidirectionalIndexOf(a, b, c, d, e){
for(c=a.length,d=c*1; c--; ){
if(a[c]==b) return c; //or this[c]===b
if(a[e=d-1-c]==b) return e; //or a[e=d-1-c]===b
}
return -1
}
//Usage
bidirectionalIndexOf(array,'value');
http://jsperf.com/bidirectionalindexof
Ca test am creat un tablou cu 100 de mii de intrări.
Trei întrebări: la începutul, la mijlocul & la sfârșitul șirului.
Sper că veți găsi, de asemenea, interesant și de testare de performanță.
Notă: după Cum puteți vedea am modificat ușor "conține" funcția de a reflecta indexOf & lastIndexOf de ieșire (deci, practic, "adevărat", cu "index" și "fals" cu -1
). Care ar trebui't rău.
Object.defineProperty(Array.prototype,'bidirectionalIndexOf',{value:function(b,c,d,e){
for(c=this.length,d=c*1; c--; ){
if(this[c]==b) return c; //or this[c]===b
if(this[e=d-1-c] == b) return e; //or this[e=d-1-c]===b
}
return -1
},writable:false, enumerable:false});
// Usage
array.bidirectionalIndexOf('value');
Funcția poate fi, de asemenea, ușor de modificat pentru a returna true sau false sau chiar la obiect, șir de caractere sau orice altceva.
Și aici este în timp ce
varianta:
function bidirectionalIndexOf(a, b, c, d){
c=a.length; d=c-1;
while(c--){
if(b===a[c]) return c;
if(b===a[d-c]) return d-c;
}
return c
}
// Usage
bidirectionalIndexOf(array,'value');
Cred că la un calcul simplu pentru a obține reflectate index într-o matrice este atât de simplu pe care-l's de două ori mai rapid decât a face o reală bucla de repetare.
Aici este un exemplu complex de trei controale pe iterație, dar acest lucru este posibil numai cu un mai de calcul care determină încetinirea din cod.
Dacă utilizați JavaScript 1.6 sau mai târziu (Firefox 1.5 sau mai târziu), se poate utiliza Matrice.indexOf. Altfel, cred că aveți de gând să se încheie cu ceva similar cu codul original.
Dacă sunteți de verificare în mod repetat pentru existența unui obiect într-o matrice poate că ar trebui să se uite în
Vom folosi acest fragment (funcționează cu obiecte, tablouri, siruri de caractere):
/*
* @function
* @name Object.prototype.inArray
* @description Extend Object prototype within inArray function
*
* @param {mix} needle - Search-able needle
* @param {bool} searchInKey - Search needle in keys?
*
*/
Object.defineProperty(Object.prototype, 'inArray',{
value: function(needle, searchInKey){
var object = this;
if( Object.prototype.toString.call(needle) === '[object Object]' ||
Object.prototype.toString.call(needle) === '[object Array]'){
needle = JSON.stringify(needle);
}
return Object.keys(object).some(function(key){
var value = object[key];
if( Object.prototype.toString.call(value) === '[object Object]' ||
Object.prototype.toString.call(value) === '[object Array]'){
value = JSON.stringify(value);
}
if(searchInKey){
if(value === needle || key === needle){
return true;
}
}else{
if(value === needle){
return true;
}
}
});
},
writable: true,
configurable: true,
enumerable: false
});
Utilizare:
var a = {one: "first", two: "second", foo: {three: "third"}};
a.inArray("first"); //true
a.inArray("foo"); //false
a.inArray("foo", true); //true - search by keys
a.inArray({three: "third"}); //true
var b = ["one", "two", "three", "four", {foo: 'val'}];
b.inArray("one"); //true
b.inArray('foo'); //false
b.inArray({foo: 'val'}) //true
b.inArray("{foo: 'val'}") //false
var c = "String";
c.inArray("S"); //true
c.inArray("s"); //false
c.inArray("2", true); //true
c.inArray("20", true); //false
Soluție care funcționează în toate browserele moderne:
function contains(arr, obj) {
const stringifiedObj = JSON.stringify(obj); // Cache our object to not call `JSON.stringify` on every iteration
return arr.some(item => JSON.stringify(item) === stringifiedObj);
}
Utilizare:
contains([{a: 1}, {a: 2}], {a: 1}); // true
IE6+ soluție:
function contains(arr, obj) {
var stringifiedObj = JSON.stringify(obj)
return arr.some(function (item) {
return JSON.stringify(item) === stringifiedObj;
});
}
// .some polyfill, not needed for IE9+
if (!('some' in Array.prototype)) {
Array.prototype.some = function (tester, that /*opt*/) {
for (var i = 0, n = this.length; i < n; i++) {
if (i in this && tester.call(that, this[i], i, this)) return true;
} return false;
};
}
Utilizare:
contains([{a: 1}, {a: 2}], {a: 1}); // true
Matrice.indexOf " și " Matrice.include
(precum și cele mai multe răspunsuri aici) compara doar de referință și nu prin valoare.
[{a: 1}, {a: 2}].includes({a: 1});
// false, because {a: 1} is a new object
Non-optimizat ES6-o linie:
[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));
// true
Notă: Compararea obiectelor de valoare va funcționa mai bine dacă cheile sunt în aceeași ordine, astfel încât să fie sigur ai putea la fel cheile prima cu un pachet ca asta: https://www.npmjs.com/package/sort-keys
Actualizat "conține" funcția cu un perf optimizare. Multumesc itinance pentru sugestie.
Utilizarea lodash's unele funcție.
L's concise, exacte și are o mare cruce platformă de sprijin.
La răspunsul acceptat nici măcar nu îndeplinește cerințele.
Cerinte: Recomanda cele mai concis și eficient mod de a afla dacă o matrice JavaScript conține un obiect.
Acceptat Răspunsul:
$.inArray({'b': 2}, [{'a': 1}, {'b': 2}])
> -1
Recomandarea mea:
_.some([{'a': 1}, {'b': 2}], {'b': 2})
> true
Note:
$.inArray funcționează bine pentru a stabili dacă o scalar valoare există într-o serie de scalari...
$.inArray(2, [1,2])
> 1
... dar întrebarea în mod clar solicită o modalitate eficientă de a determina dacă o obiect sunt conținute într-o matrice.
În scopul de a gestiona atât scalari și obiecte, ai putea face acest lucru:
(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)