Przypisania zmiennych JavaScript z krotek

101

W innych językach, takich jak Python 2 i Python 3, możesz definiować i przypisywać wartości do zmiennej krotkiej oraz pobierać ich wartości w następujący sposób:

tuple = ("Bob", 24)
name, age = tuple
print(name)           #name evaluates to Bob
print(age)            #age evaluates to 24

Czy jest coś podobnego w JavaScript? A może po prostu muszę to zrobić w brzydki sposób z tablicą:

tuple = ["Bob", 24]
name = tuple[0]       //name Evaluates to Bob
age = tuple[1]        //age Evaluates to 24

Czy istnieje lepszy sposób na symulowanie krotek Pythona w JavaScript 5?

Aktualizacja: Zobacz odpowiedź dotyczącą ES6, która powinna być preferowana w nowych projektach od CoffeeScript.

Karl
źródło
12
W JavaScript nie zapomnij zadeklarować zmiennych:var tuple, name, age;
Šime Vidas
3
var name=tuple[0], age=tuple[1]; To trochę więcej pisania, ale brzydota może być przesadą.
Brent Bradburn,

Odpowiedzi:

122

Javascript 1.7 dodał zniszczone przypisanie, które pozwala robić zasadniczo to, czego szukasz.

function getTuple(){
   return ["Bob", 24];
}
var [a, b] = getTuple();
// a === "bob" , b === 24 are both true
pc1oad1etter
źródło
5
Nie jest to oparty na standardach javascript, ale raczej rozszerzenia specyficzne dla Mozilli.
ninjagecko
14
@ninjagecko: „JavaScript” to implementacja Mozilli, a zadania destrukturyzujące będą częścią nadchodzącego standardu ekmaskryptu
Bergi
65
Obecnie jest w rzeczywistości częścią ES6.
Pier Paolo Ramon
10
Po kilku latach moja odpowiedź stała się technicznie poprawna, ale nie była pomocna ani poprawna, ani pomocna. Yay!
pc1oad1etter
49

Musisz to zrobić w brzydki sposób. Jeśli naprawdę chcesz czegoś takiego, możesz sprawdzić CoffeeScript , który ma to i wiele innych funkcji, które sprawiają, że wygląda bardziej jak Python (przepraszam, że brzmi to jak reklama, ale naprawdę to lubię).

Gabi Purcaru
źródło
Hmm, to całkiem interesujące, właśnie sam wpadłem na ten scenariusz, ponieważ JavaScript nie wydaje się zapewniać oczywistej obsługi krotek, a pochodząc z intensywnego okresu programowania funkcjonalnego, rozwijasz potrzebę krotek. Właśnie to znalazłem, ale nie jestem pewien, czy działa, po prostu wyglądało dobrze również pod względem obsługi krotek: cs.umd.edu/projects/PL/arrowlets/api-tuples.xhtml . Na pewno spojrzę na CoffeeScript.
9codeMan9
29

Możesz zrobić coś podobnego:

var tuple = Object.freeze({ name:'Bob', age:14 })

a następnie odnosić się do imienia i wieku jako atrybutów

tuple.name 
tuple.age 
monika mevenkamp
źródło
5
Nie będę głosował przeciw, ale technicznie jest to nieprawidłowe. Po zadeklarowaniu obiektu nadal można zmieniać (tj. Mutować) wartości tuple.name i tuple.age. Z definicji niezmienne typy nie mogą zostać zmienione po ich zdefiniowaniu. Przypominają typy tylko do odczytu, w których zarówno parametry, jak i ich wartości można zadeklarować tylko raz.
Evan Plaice
4
@EvanPlaice jeśli problemem jest zmienność, możesz użyć Object.freeze(), np .:tuple = Object.freeze({ name:'Bob', age:14 })
canon
@canon Zgadzam się, to chyba jedyne dopuszczalne / poprawne podejście w całym tym wątku. Niestety odpowiedź mcm nie powoduje zamrożenia obiektu, więc nadal można go modyfikować.
Evan Plaice
@EvanPlaice Nie widzę, skąd wziął się problem zmienności - krotki nie są niezmienne w oryginalnym przykładzie Pythona!
Daniel Buckmaster
@DanielBuckmaster Różnica między listą a krotką w Pythonie polega na tym, że lista jest zmienna, a krotka nie. Zobacz docs.python.org/2/tutorial/… . Krotki nie są natywnie obsługiwane w JavaScript, ponieważ wszystkie struktury danych JS są mutowalne, chyba że wywołasz Object.freeze () na obiekcie po utworzeniu.
Evan Plaice
27

Ta funkcja „krotki” w EcmaScript 2015 nazywa się destrukturyzacją i wkrótce ma być obsługiwana przez najnowsze przeglądarki. Na razie obsługują go tylko Firefox i Chrome .

Ale hej, możesz użyć transpilera .

Kod wyglądałby tak ładnie jak python:

let tuple = ["Bob", 24]
let [name, age] = tuple

console.log(name)
console.log(age)
Adrian
źródło
2
Dla przyszłych czytelników: ta funkcja jest obsługiwana w Chrome od wersji Chrome 49 (zgodnie z dokumentacją Mozilli). Możesz również sprawdzić zgodność, korzystając z dokumentacji Mozilli tutaj: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Jamie
13

Zamrożona tablica zachowuje się identycznie jak krotka Pythona:

const tuple = Object.freeze(["Bob", 24]);
let [name, age]; = tuple
console.debug(name); // "Bob"
console.debug(age); // 24

Bądź fantazyjny i zdefiniuj klasę

class Tuple extends Array { 
  constructor(...items) { 
    super(...items); 
    Object.freeze(this);
  } 
}

let tuple = new Tuple("Jim", 35);
let [name, age] = tuple;
console.debug(name); // Jim
console.debug(age); // 35
tuple = ["Bob", 24]; // no effect 
console.debug(name); // Jim
console.debug(age); // 25

Działa dzisiaj we wszystkich najnowszych przeglądarkach.

Matthew James Davis
źródło
Nie mógłbyś po prostu ustawić go na const? Stała jest niezmienna, prawda?
Mark A
3
Nie. Stałej nie można ponownie przypisać. Nie możesz zrobić const tuple = ["Jim", 35]; tuple = ["Jakub", 35]. Możesz zrobić const tuple = ["Jim", 35]; tuple [0] = "Jakub"; Dlatego nie jest niezmienna.
Matthew James Davis,
6

Krotki nie są obsługiwane w JavaScript

Jeśli szukasz niezmiennej listy, możesz użyć Object.freeze (), aby uczynić tablicę niezmienną.

Metoda Object.freeze () zamraża obiekt: to znaczy zapobiega dodawaniu do niego nowych właściwości; zapobiega usuwaniu istniejących nieruchomości; i zapobiega zmianie istniejących właściwości lub ich wyliczalności, konfigurowalności lub zapisywalności. W istocie przedmiot staje się skutecznie niezmienny. Metoda zwraca zamrożony obiekt.

Źródło: Mozilla Developer Network - Object.freeze ()

Przypisz tablicę jak zwykle, ale zablokuj ją za pomocą 'Object.freeze ()

> tuple = Object.freeze(['Bob', 24]);
[ 'Bob', 24 ]

Używaj wartości tak, jak w przypadku zwykłej tablicy (wielokrotne przypisanie w Pythonie nie jest obsługiwane)

> name = tuple[0]
'Bob'
> age = tuple[1]
24

Spróbuj przypisać nową wartość

> tuple[0] = 'Steve'
'Steve'

Ale wartość się nie zmienia

> console.log(tuple)
[ 'Bob', 24 ]
Evan Plaice
źródło
Na marginesie miejmy nadzieję, że krotki otrzymają najwyższej klasy wsparcie w ES6. Prawdziwa natywna krotka (tj. Sekwencja heterogeniczna) również poprawi szybkość.
Evan Plaice
5

Niestety nie możesz użyć tej składni przypisania krotki w skrypcie (ECMA | Java).

EDYCJA: Ktoś powiązany z Mozillą / JS 1.7 - to nie działałoby w różnych przeglądarkach, ale jeśli nie jest to wymagane, to jest twoja odpowiedź.

meder omuraliev
źródło
3

To nie jest przeznaczone do rzeczywistego wykorzystania, tylko interesujące ćwiczenie. Zobacz Dlaczego używanie funkcji eval JavaScript to zły pomysł? dla szczegółów.

Jest to najbliższe rozwiązanie, jakie można uzyskać bez uciekania się do rozszerzeń specyficznych dla dostawcy:

myArray = [1,2,3];
eval(set('a,b,c = myArray'));

Funkcja pomocnicza:

function set(code) {
    var vars=code.split('=')[0].trim().split(',');
    var array=code.split('=')[1].trim();
    return 'var '+vars.map(function(x,i){return x+'='+array+'['+i+']'}).join(',');
}

Dowód, że działa w dowolnym zakresie:

(function(){
    myArray = [4,5,6];
    eval(set('x,y,z = myArray'));
    console.log(y);  // prints 5
})()

eval nie jest obsługiwany w przeglądarce Safari.

ninjagecko
źródło
5
+1 za bycie super sprytnym, jednak nigdy nie używaj tego w prawdziwym życiu :-p
Jamund Ferguson,
3

Jako aktualizacja odpowiedzi ministra możesz to teraz zrobić za pomocą es2015:

function Tuple(...args) {
  args.forEach((val, idx) => 
    Object.defineProperty(this, "item"+idx, { get: () => val })
  )
}


var t = new Tuple("a", 123)
console.log(t.item0) // "a"
t.item0 = "b"
console.log(t.item0) // "a"

https://jsbin.com/fubaluwimo/edit?js,console

Zach Dahl
źródło
Nie ma powodu, dla którego nie mogłeś tego zrobić przed ES2015 ... Również to nie odpowiada na pytanie OP, który poprosił o destrukturyzację
ThatWeirdo
3

Możesz również mieć typ krotki w JavaScript. Po prostu zdefiniuj to za pomocą funkcji wyższego rzędu (termin akademicki to kodowanie Kościoła):

const Tuple = (...args) => {
  const Tuple = f => f(...args);
  return Object.freeze(Object.assign(Tuple, args));
};

const get1 = tx => tx((x, y) => x);

const get2 = tx => tx((x, y) => y);

const bimap = f => g => tx => tx((x, y) => Tuple(f(x), g(y)));

const toArray = tx => tx((...args) => args);

// aux functions

const inc = x => x + 1;
const toUpperCase = x => x.toUpperCase();

// mock data

const pair = Tuple(1, "a");

// application

console.assert(get1(pair) === 1);
console.assert(get2(pair) === "a");

const {0:x, 1:y} = pair;
console.log(x, y); // 1 a

console.log(toArray(bimap(inc) (toUpperCase) (pair))); // [2, "A"]

const map = new Map([Tuple(1, "a"), Tuple(2, "b")]);
console.log(map.get(1), map.get(2)); // a b

Pamiętaj, że Tuplenie jest używany jako normalny konstruktor. Rozwiązanie wcale nie opiera się na systemie prototypowym, ale wyłącznie na funkcjach wyższego rzędu.

Jakie są zalety krotek Arrayużywanych jak krotki? Krotki kodowane w kościele są niezmienne z założenia i zapobiegają skutkom ubocznym powodowanym przez mutacje. Pomaga to w tworzeniu bardziej niezawodnych aplikacji. Ponadto łatwiej jest wnioskować o kodzie, który rozróżnia Arrays jako typ kolekcji (np. [a]) I krotki jako powiązane dane różnych typów (np (a, b).).


źródło
1

Oto prosta implementacja krotki Javascript:

var Tuple = (function () {
   function Tuple(Item1, Item2) {
      var item1 = Item1;
      var item2 = Item2;
      Object.defineProperty(this, "Item1", {
          get: function() { return item1  }
      });
      Object.defineProperty(this, "Item2", {
          get: function() { return item2  }
      });
   }
   return Tuple;
})();

var tuple = new Tuple("Bob", 25); // Instantiation of a new Tuple
var name = tuple.Item1; // Assignment. name will be "Bob"
tuple.Item1 = "Kirk"; // Will not set it. It's immutable.

To jest 2 krotka, jednak możesz zmodyfikować mój przykład, aby obsługiwał krotki 3,4,5,6 itp.

Faris Zacina
źródło
Właściwie to nie jest t-uple, ale para.
Pier Paolo Ramon
tupleInstancja jest wciąż zmienny, więc nie jest to technicznie krotka. Na dowód zmiany tuple.Item1 = "Steve"ówczesnego console.log()wyjściu.
Evan Plaice
1
Dzięki za znalezienie tego. Zmodyfikowałem przykład, aby obiekt Tuple był niezmienny.
Faris Zacina
Jak zmodyfikować to, aby obsługiwać dowolną długość?
aij
To nie odpowiada na pytanie OP, który poprosił o destrukturyzację
ThatWeirdo
0

Oto wersja odpowiedzi Matthew Jamesa Davisa z dodanymi metodami krotek w języku Python:

class Tuple extends Array { 
  constructor(...items) { 
    super(...items); 
    Object.freeze(this);
  }
  toArray() {
    return [...this];
  }
  toString() {
    return '('+super.toString()+')';
  }
  count(item) {
    var arr = this.toArray();
    var result = 0;
    for(var i = 0; i < arr.length; i++) {
       if(arr[i] === item) {
         result++;
       }
    }
    return result;
  }

  

  
}

   let tuple = new Tuple("Jim", 35);
   let [name,age] = tuple;

console.log("tuple:"+tuple)
console.log("name:"+name)
console.log("age:"+age)

Nirwana
źródło