Sprawdź równość zmiennych na liście wartości

141

Na przykład sprawdzam zmienną pod fookątem równości z pewną liczbą wartości. Na przykład,

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

Chodzi o to, że jest to dość dużo kodu do tak banalnego zadania. Wymyśliłem:

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

ale też to nie do końca mi się podoba, ponieważ muszę nadać zbędne wartości elementom w obiekcie.

Czy ktoś zna przyzwoity sposób sprawdzania równości względem wielu wartości?

pimvdb
źródło
Myślę, że przy podejmowaniu decyzji o czymś takim należy uwzględnić szerszy kontekst, ponieważ ważne jest, aby wiedzieć, dlaczego dokonujesz takich porównań.
Pointy
Cóż, w grze, którą tworzę, sprawdzam kody klawiatury, aby zdecydować, jaką funkcję należy wywołać. W różnych przeglądarkach klucz ma pozornie różne kody kluczy, stąd potrzeba porównania z wieloma wartościami.
pimvdb
sprawdź test wydajności dla wielu metod, operator logiczny wygrywa runkit.com/pramendra/58cad911146c1c00147f8d8d
Pramendra Gupta

Odpowiedzi:

186

Możesz użyć tablicy i indexOf:

if ([1,3,12].indexOf(foo) > -1)
Gumbo
źródło
1
Podoba mi się ten. Możliwe byłoby nawet utworzenie funkcji „zawiera” za pomocą prototypu, jak sądzę, aby wyeliminować użycie> -1.
pimvdb
1
@pimvdb: Zauważ, że możesz potrzebować własnej implementacji, która indexOfjest dostępna tylko od piątej edycji ECMAScript.
Gumbo
W innej odpowiedzi również o tym wspomniano, nie wiedziałem tego, dzięki
pimvdb
Czy „>” jest w jakiś sposób lepsze niż „! ==”?
Max Waterman
127

W ECMA2016 można użyć zawiera metodę. To najczystszy sposób, jaki widziałem. (Obsługiwane przez wszystkie główne przeglądarki, z wyjątkiem IE (Polyfill jest w linku)

if([1,3,12].includes(foo)) {
    // ...
}
Alister
źródło
4
Dla zmiennych if([a,b,c].includes(foo))lub ciągówif(['a','b','c'].includes(foo))
Hastig Zusammenstellen
dla zmiennych => if([a,b,c].includes(foo))ponieważ cudzysłów sprawi, że będzie to ciąg znaków. @HastigZusammenstellen
iambinodstha
2
@BinodShrestha Myślę, że tak właśnie napisałem, chyba że czegoś mi brakuje. „Dla zmiennych… lub ciągów…”
Hastig Zusammenstellen
14

Korzystając z udzielonych odpowiedzi, otrzymałem następujące informacje:

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

Można to nazwać tak:

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

Edycja: ostatnio natknąłem się na tę „sztuczkę”, która jest przydatna, jeśli wartości są ciągami znaków i nie zawierają znaków specjalnych. W przypadku znaków specjalnych staje się brzydki z powodu ucieczki, a także z tego powodu jest bardziej podatny na błędy.

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

Mówiąc dokładniej, spowoduje to sprawdzenie dokładnego ciągu, ale z drugiej strony jest to bardziej skomplikowane w przypadku prostego testu równości:

/^(foo|bar|something)$/.test(str);
pimvdb
źródło
5
Dobroć! Jestem zdumiony, że działa - into słowo kluczowe w Javascript. Na przykład uruchomienie function in() {}; in()skutkuje błędem składni, przynajmniej w Firefoksie, i nie jestem pewien, dlaczego twój kod nie. :-)
Ben Blank
4
Ponadto przedłużanie jest często uważane za złą praktykę Object.prototype, ponieważ wpływa to for (foo in bar)w niefortunny sposób. Może zmienić to na function contains(obj) { for (var i = 1; i < arguments.length; i++) if (arguments[i] === obj) return true; return false; }i przekazać obiekt jako argument?
Ben Blank
Dziękuję za twoją reakcję, a Google Closure Compiler również zwraca błąd podczas kompilacji, podczas gdy sam ten kod działa pięknie. W każdym razie zasugerowano, że klasa pomocnicza może wykonać tę pracę lepiej niż rozszerzanie, o Object.prototypeczym również wspomniałeś. Jednak wolę ten sposób, ponieważ notacja do sprawdzania jest piękna, po prostu foo.in(1, 2, "test", Infinity), łatwa jak ciasto.
pimvdb
Definiowanie funkcji in()w zwykły sposób również zawodzi w Chrome, ale prototyp działa ... :)
pimvdb
3
Rozszerzenie Object.prototype jest anty-wzorcem i nie powinno być używane. Zamiast tego użyj funkcji.
Vernon,
9

Można napisać if(foo in L(10,20,30)), jeśli zdefiniować Ljako

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

    return obj;
};
Elian Ebbing
źródło
To jest jeszcze wyraźniejsze niż poprzednie odpowiedzi, dzięki!
pimvdb
2
Myślę, że rozsądnie jest zauważyć, że funkcje prototypowe są również `` w '' tablicy / obiekcie - więc jeśli w prototypie tablicy / obiektu znajduje się funkcja `` usuń '', zawsze zwróci ona wartość true, jeśli kodujesz 'remove' in L(1, 3, 12), chociaż tego nie zrobiłeś określić „usuń”, aby umieścić je na liście.
pimvdb
7

Jest to łatwo rozszerzalne i czytelne:

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

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

Ale niekoniecznie łatwiej :)

orlp
źródło
Właściwie użyłem tej metody, ponieważ chciałem umieścić komentarz obok każdej wartości, aby opisać, z czym jest związana wartość, był to najbardziej czytelny sposób, aby to osiągnąć.
pholcroft
@pholcroft, jeśli chcesz opisać, do czego odnosi się każda wartość, utwórz wyliczenie lub klasę i napisz swoje właściwości tak, aby miały nazwy opisowe.
red_dorian
6
var a = [1,2,3];

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

Zauważ, że indexOf nie znajduje się w podstawowym skrypcie ECMAScript. Będziesz potrzebował fragmentu kodu dla IE i być może innych przeglądarek, które nie obsługują 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;
  };
}
meder omuraliev
źródło
Jest to bezpośrednio kopia Mozilli. Był zbyt leniwy, by utworzyć link. developer.mozilla.org/en/JavaScript/Reference/Global_Objects/ ...
meder omuraliev
Czy istnieje powód do przesunięcia bitów, gdy definiujesz len, jeśli w rzeczywistości nie przesuwasz bitów? Czy zachodzi tu jakiś ukryty przymus?
jaredad7
5

Teraz możesz mieć lepsze rozwiązanie, aby rozwiązać ten scenariusz, ale w inny sposób, który wolałem.

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    
}

Preferowałem powyższe rozwiązanie niż sprawdzanie indexOf, gdzie również musisz sprawdzić index.

obejmuje dokumenty

if ( arr.indexOf( foo ) !== -1 ) { }
Javed Shaikh
źródło
3

Ponadto, ponieważ wszystkie wartości, względem których sprawdzasz wynik, są unikalne, możesz również użyć metody 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) );

Ivan Sivak
źródło
1

Podobała mi się zaakceptowana odpowiedź, ale pomyślałem, że fajnie byłoby umożliwić jej również przyjmowanie tablic, więc rozszerzyłem ją do tego:

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

Możesz usunąć wymuszenie typu, zmieniając ==na ===.

Greg Perham
źródło
1

Jeśli masz dostęp do podkreślenia, możesz użyć:

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

contains kiedyś pracowałeś w Lodash (przed V4), teraz musisz użyć includes

if (_.includes([1, 3, 12], foo)) {
  handleYellowFruit();
}
David Salamon
źródło
1

To jest mała pomocnicza funkcja strzałki:

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

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

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

Więcej informacji tutaj ...

northwind
źródło
Bardzo dobre podejście: zaczynając od swojego pomysłu możesz to również zrobić bez funkcji pomocniczej:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].some(option => option === valToCheck); // false
Giorgio Tempesta
Ale kiedy zacząłem poprawiać kod, który znalazłem, includesstaje się lepszą opcją, jak w odpowiedzi @ alistera:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].includes(valToCheck); // false
Giorgio Tempesta
0

Po prostu użyłem funkcji jQuery inArray i tablicy wartości, aby to osiągnąć:

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

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

if(test >= 0) // true
ScottyG
źródło