Controlla l'uguaglianza delle variabili rispetto a un elenco di valori


133

Sto verificando una variabile, diciamo foo, per l'uguaglianza con un numero di valori. Per esempio,

if( foo == 1 || foo == 3 || foo == 12 ) {
    // ...
}

Il punto è che si tratta piuttosto di un codice per un compito così banale. Ho pensato a quanto segue:

if( foo in {1: 1, 3: 1, 12: 1} ) {
    // ...
}

ma anche questo non mi piace completamente, perché devo dare valori ridondanti agli oggetti nell'oggetto.

Qualcuno conosce un modo decente di fare un controllo di uguaglianza rispetto a più valori?


Penso che il contesto più ampio debba essere coinvolto quando si decide qualcosa di simile, perché è importante sapere perché si stanno facendo questi confronti.
Punta il

Bene, in un gioco che sto creando sto controllando i codici della tastiera in modo da decidere quale funzione deve essere chiamata. In diversi browser, una chiave ha diversi codici chiave apparentemente, quindi la necessità di confrontare con più valori.
pimvdb,

verifica il test delle prestazioni per più metodi, l'operatore logico vince runkit.com/pramendra/58cad911146c1c00147f8d8d
Pramendra Gupta

Risposte:


175

È possibile utilizzare un array e indexOf:

if ([1,3,12].indexOf(foo) > -1)

1
Mi piace questa. Sarebbe anche possibile creare una funzione 'contiene' attraverso il prototipo, suppongo, in modo da eliminare l'uso di> -1.
pimvdb,

1
@pimvdb: tieni presente che potresti aver bisogno della tua implementazione poiché indexOfè disponibile solo dalla quinta edizione di ECMAScript.
Gumbo,

In un'altra risposta è menzionato anche questo, non lo sapevo, grazie
pimvdb,

'>' È in qualche modo migliore di '! =='?
Max Waterman,

117

In ECMA2016 è possibile utilizzare il metodo include . È il modo più pulito che abbia mai visto. (Supportato da tutti i principali browser, tranne IE (Polyfill è nel link)

if([1,3,12].includes(foo)) {
    // ...
}

4
Per variabili if([a,b,c].includes(foo))o stringheif(['a','b','c'].includes(foo))
Hastig Zusammenstellen

per variabili => if([a,b,c].includes(foo))perché le virgolette lo renderanno stringa. @HastigZusammenstellen
iambinodstha,

2
@BinodShrestha Penso che sia quello che ho scritto, a meno che non mi manchi qualcosa. "Per variabili .. o stringhe ..."
Hastig Zusammenstellen,

16

Usando le risposte fornite, ho finito con il seguente:

Object.prototype.in = function() {
    for(var i=0; i<arguments.length; i++)
       if(arguments[i] == this) return true;
    return false;
}

Può essere chiamato come:

if(foo.in(1, 3, 12)) {
    // ...
}

Modifica: ultimamente mi sono imbattuto in questo 'trucco' che è utile se i valori sono stringhe e non contengono caratteri speciali. Per i personaggi speciali diventa brutto a causa della fuga ed è anche più soggetto a errori a causa di ciò.

/foo|bar|something/.test(str);

Per essere più precisi, questo controllerà la stringa esatta, ma poi è ancora più complicato per un semplice test di uguaglianza:

/^(foo|bar|something)$/.test(str);

5
Bontà! Sono stupito che funzioni: inè una parola chiave in Javascript. Ad esempio, l'esecuzione function in() {}; in()causa un errore di sintassi, almeno in Firefox, e non sono sicuro del perché il codice non lo sia. :-)
Ben Blank

3
Inoltre, è spesso considerata una cattiva pratica da estendere Object.prototype, poiché influisce for (foo in bar)in modo sfortunato. Forse cambiarlo in function contains(obj) { for (var i = 1; i < arguments.length; i++) if (arguments[i] === obj) return true; return false; }e passare l'oggetto come argomento?
Ben Blank

Grazie per la tua reazione, e anche il compilatore di chiusura di Google restituisce un errore durante la compilazione mentre questo codice stesso funziona perfettamente. Ad ogni modo, è stato suggerito che una classe di supporto può fare questo lavoro meglio che estenderlo, Object.prototypecome hai anche detto. Tuttavia, preferisco così, poiché la notazione per il controllo è adorabile, semplicemente foo.in(1, 2, "test", Infinity), semplice come una torta.
pimvdb,

E definire una funzione in in()modo regolare non riesce anche su Chrome, ma il prototipo funziona ... :)
pimvdb

3
L'estensione di Object.prototype è un anti-pattern e non deve essere utilizzato. Utilizzare invece una funzione.
Vernon,

9

È possibile scrivere if(foo in L(10,20,30))se si definisce Ldi essere

var L = function()
{
    var obj = {};
    for(var i=0; i<arguments.length; i++)
        obj[arguments[i]] = null;

    return obj;
};

È ancora più chiaro delle prime risposte, grazie!
pimvdb,

2
Penso che sia prudente osservare che le funzioni del prototipo sono anche 'in' un array / oggetto - quindi se c'è una funzione 'rimuovi' nel prototipo di un array / oggetto, tornerà sempre vero se si codifica 'remove' in L(1, 3, 12), anche se non lo si è fatto specifica 'rimuovi' da inserire nell'elenco.
pimvdb,

7

Questo è facilmente estensibile e leggibile:

switch (foo) {
    case 1:
    case 3:
    case 12:
        // ...
        break

    case 4:
    case 5:
    case 6:
        // something else
        break
}

Ma non necessariamente più semplice :)


In realtà ho usato questo metodo perché volevo mettere un commento accanto a ciascun valore per descrivere a quale valore si riferiva, questo era il modo più leggibile per raggiungerlo.
pholcroft,

@pholcroft se hai bisogno di descrivere a cosa si riferisce ogni valore, dovresti creare un enum o una classe e scrivere le tue proprietà per avere nomi descrittivi.
red_dorian,

6
var a = [1,2,3];

if ( a.indexOf( 1 ) !== -1 ) { }

Si noti che indexOf non è nel core ECMAScript. Dovrai avere uno snippet per IE e possibilmente altri browser che non supportano Array.prototype.indexOf.

if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(searchElement /*, fromIndex */)
  {
    "use strict";

    if (this === void 0 || this === null)
      throw new TypeError();

    var t = Object(this);
    var len = t.length >>> 0;
    if (len === 0)
      return -1;

    var n = 0;
    if (arguments.length > 0)
    {
      n = Number(arguments[1]);
      if (n !== n)
        n = 0;
      else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
        n = (n > 0 || -1) * Math.floor(Math.abs(n));
    }

    if (n >= len)
      return -1;

    var k = n >= 0
          ? n
          : Math.max(len - Math.abs(n), 0);

    for (; k < len; k++)
    {
      if (k in t && t[k] === searchElement)
        return k;
    }
    return -1;
  };
}

È la copia di Mozilla direttamente. Era troppo pigro per il collegamento. developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
meder omuraliev

C'è un motivo per lo spostamento dei bit quando si definisce len se in realtà non si stanno spostando i bit? C'è qualche coercizione implicita in corso qui?
jaredad7,

3

Inoltre, poiché i valori in base ai quali si sta verificando il risultato sono tutti unici, è possibile utilizzare anche Set.prototype.has () .

var valid = [1, 3, 12];
var goodFoo = 3;
var badFoo = 55;

// Test
console.log( new Set(valid).has(goodFoo) );
console.log( new Set(valid).has(badFoo) );


2

Ora potresti avere una soluzione migliore per risolvere questo scenario, ma in un altro modo che ho preferito.

const arr = [1,3,12]
if( arr.includes(foo)) { // it will return true if you `foo` is one of array values else false
  // code here    
}

Ho preferito la soluzione sopra all'indice di controllo dove è necessario controllare anche l'indice.

include documenti

if ( arr.indexOf( foo ) !== -1 ) { }

1

Mi è piaciuta la risposta accettata, ma ho pensato che sarebbe stato utile consentirle di prendere anche array, quindi l'ho estesa a questo:

Object.prototype.isin = function() {
    for(var i = arguments.length; i--;) {
        var a = arguments[i];
        if(a.constructor === Array) {
            for(var j = a.length; j--;)
                if(a[j] == this) return true;
        }
        else if(a == this) return true;
    }
    return false;
}

var lucky = 7,
    more = [7, 11, 42];
lucky.isin(2, 3, 5, 8, more) //true

Puoi rimuovere la coercizione del tipo cambiando ==in ===.


1

Se hai accesso a Underscore, puoi utilizzare quanto segue:

if (_.contains([1, 3, 12], foo)) {
  // ...
}

contains lavorava anche a Lodash (prima di V4), ora devi usare includes

if (_.includes([1, 3, 12], foo)) {
  handleYellowFruit();
}

1

Questa è una piccola funzione di aiuto:

const letters = ['A', 'B', 'C', 'D'];

function checkInList(arr, val) {
  return arr.some(arrVal => val === arrVal);
}

checkInList(letters, 'E');   // false
checkInList(letters, 'A');   // true

Maggiori informazioni qui ...


Ottimo approccio: a partire dalla tua idea puoi anche farlo senza una funzione di supporto:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].some(option => option === valToCheck); // false
Giorgio Tempesta,

Ma una volta che ho iniziato a modificare il codice, ho scoperto che includesdiventa l'opzione migliore, come nella risposta di @ alister:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].includes(valToCheck); // false
Giorgio Tempesta,

0

Ho semplicemente usato la funzione jArery inArray e una matrice di valori per ottenere questo risultato:

myArr = ["A", "B", "C", "C-"];

var test = $.inArray("C", myArr)  
// returns index of 2 returns -1 if not in array

if(test >= 0) // true

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.