JavaScript-Variablenzuweisungen aus Tupeln

97

In anderen Sprachen wie Python 2 und Python 3 können Sie einer Tupelvariablen Werte definieren, diese zuweisen und ihre Werte wie folgt abrufen:

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

Gibt es etwas Ähnliches in JavaScript? Oder muss ich es einfach hässlich mit einem Array machen:

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

Gibt es eine bessere Möglichkeit, Python-Tupel in JavaScript 5 zu simulieren?

Update: Siehe die Antwort zu ES6, die bei neuen Projekten gegenüber CoffeeScript bevorzugt werden sollte.

Karl
quelle
12
Vergessen Sie in JavaScript nicht, die Variablen zu deklarieren:var tuple, name, age;
Šime Vidas
3
var name=tuple[0], age=tuple[1]; Es ist ein bisschen mehr tippen, aber hässlich könnte eine Übertreibung sein.
Brent Bradburn

Antworten:

118

Javascript 1.7 hat eine zerstörte Zuordnung hinzugefügt , mit der Sie im Wesentlichen das tun können, wonach Sie suchen.

function getTuple(){
   return ["Bob", 24];
}
var [a, b] = getTuple();
// a === "bob" , b === 24 are both true
pc1oad1etter
quelle
5
Dies ist kein standardbasiertes Javascript, sondern Mozilla-spezifische Erweiterungen.
Ninjagecko
14
@ninjagecko: "JavaScript" ist Mozillas Implementierung, und Destrukturierungsaufgaben werden Teil des kommenden Ecmascript-Standards sein
Bergi
64
Es ist jetzt tatsächlich Teil von ES6.
Pier Paolo Ramon
9
In ein paar Jahren ist meine Antwort technisch korrekt geworden, aber nicht korrekt, sowohl korrekt als auch hilfreich. Yay!
pc1oad1etter
49

Du musst es auf hässliche Weise tun. Wenn Sie so etwas wirklich wollen, können Sie sich CoffeeScript ansehen, das das und viele andere Funktionen bietet, mit denen es eher wie Python aussieht (es tut mir leid, dass es wie eine Werbung klingt, aber ich mag es wirklich.)

Gabi Purcaru
quelle
Hmm, das ist ziemlich interessant, ich bin gerade auf dieses Szenario gestoßen, da JavaScript keine offensichtliche Tupelunterstützung zu bieten scheint und Sie aus einer intensiven Phase der funktionalen Programmierung den Wunsch nach Tupeln entwickeln. Ich habe das auch gerade gefunden, bin mir aber nicht sicher, ob es funktioniert. Es sah auch in Bezug auf die Tupelunterstützung gut aus: cs.umd.edu/projects/PL/arrowlets/api-tuples.xhtml . Ich werde mir auf jeden Fall CoffeeScript ansehen.
9codeMan9
29

Sie können etwas Ähnliches tun:

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

und dann Name und Alter als Attribute bezeichnen

tuple.name 
tuple.age 
monika mevenkamp
quelle
5
Ich werde nicht abstimmen, aber technisch ist das falsch. Sie können die Werte von tuple.name und tuple.age nach dem Deklarieren des Objekts weiterhin ändern (dh mutieren). Per Definition können unveränderliche Typen nach ihrer Definition nicht mehr geändert werden. Sie sind wie schreibgeschützte Typen, bei denen sowohl die Parameter als auch ihre Werte nur einmal deklariert werden können.
Evan Plaice
4
@EvanPlaice Wenn Mutabilität ein Problem ist, können Sie verwenden Object.freeze(), dh:tuple = Object.freeze({ name:'Bob', age:14 })
Canon
@canon Ich stimme zu, das ist wahrscheinlich der einzig akzeptable / korrekte Ansatz in diesem ganzen Thread. Leider friert die Antwort von mcm das Objekt nicht ein, sodass es immer noch veränderlich ist.
Evan Plaice
@EvanPlaice Ich sehe nicht, woher das Problem der Veränderlichkeit kam - Tupel sind im ursprünglichen Python-Beispiel nicht unveränderlich!
Daniel Buckmaster
@DanielBuckmaster Der Unterschied zwischen einer Liste und einem Tupel in Python besteht darin, dass eine Liste veränderbar ist, ein Tupel jedoch nicht. Siehe docs.python.org/2/tutorial/… . Tupel werden in JavaScript nicht nativ unterstützt, da alle JS-Datenstrukturen veränderbar sind, es sei denn, Sie rufen Object.freeze () für das Objekt nach der Erstellung auf.
Evan Plaice
27

Diese "Tupel" -Funktion wird in EcmaScript2015 als Destrukturierung bezeichnet und wird bald von aktuellen Browsern unterstützt. Derzeit wird dies nur von Firefox und Chrome unterstützt .

Aber hey, du kannst einen Transpiler benutzen .

Der Code würde so schön aussehen wie Python:

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

console.log(name)
console.log(age)
Adrian
quelle
2
Für zukünftige Leser: Diese Funktion wird seit Chrome 49 in Chrome unterstützt (gemäß den Mozilla-Dokumenten). Sie können die Kompatibilität auch mithilfe der Mozilla-Dokumente hier überprüfen: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Jamie
12

Ein eingefrorenes Array verhält sich identisch mit einem Python-Tupel:

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

Seien Sie schick und definieren Sie eine Klasse

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

Funktioniert heute in den neuesten Browsern.

Matthew James Davis
quelle
Könnten Sie es nicht einfach auf eine Konstante setzen? Die Konstante ist unveränderlich, nein?
Markieren Sie A
2
Nein, die Konstante ist nicht neu zuweisbar. Sie können nicht const tuple = ["Jim", 35]; Tupel = ["James", 35]. Sie können const tuple = ["Jim", 35]; Tupel [0] = "James"; Daher ist es nicht unveränderlich.
Matthew James Davis
6

Tupel werden in JavaScript nicht unterstützt

Wenn Sie nach einer unveränderlichen Liste suchen, kann Object.freeze () verwendet werden, um ein Array unveränderlich zu machen.

Die Object.freeze () -Methode friert ein Objekt ein, dh es wird verhindert, dass neue Eigenschaften hinzugefügt werden. verhindert, dass vorhandene Eigenschaften entfernt werden; und verhindert, dass vorhandene Eigenschaften oder deren Aufzählbarkeit, Konfigurierbarkeit oder Beschreibbarkeit geändert werden. Im Wesentlichen wird das Objekt effektiv unveränderlich gemacht. Die Methode gibt das eingefrorene Objekt zurück.

Quelle: Mozilla Developer Network - Object.freeze ()

Weisen Sie ein Array wie gewohnt zu, aber sperren Sie es mit 'Object.freeze ()

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

Verwenden Sie die Werte wie ein normales Array (Python-Mehrfachzuweisung wird nicht unterstützt)

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

Versuchen Sie, einen neuen Wert zuzuweisen

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

Der Wert wird jedoch nicht geändert

> console.log(tuple)
[ 'Bob', 24 ]
Evan Scholle
quelle
Nebenbei bemerkt, Tuples wird hoffentlich erstklassige Unterstützung in ES6 erhalten. Ein echtes natives Tupel (dh eine heterogene Sequenz) verbessert ebenfalls die Geschwindigkeit.
Evan Plaice
5

Leider können Sie diese Tupelzuweisungssyntax nicht in (ECMA | Java) Script verwenden.

BEARBEITEN: Jemand, der mit Mozilla / JS 1.7 verbunden ist - dies würde nicht browserübergreifend funktionieren, aber wenn dies nicht erforderlich ist, gibt es Ihre Antwort.

meder omuraliev
quelle
3

Dies ist nicht dazu gedacht, tatsächlich im wirklichen Leben verwendet zu werden, sondern nur eine interessante Übung. Siehe Warum ist die Verwendung der JavaScript-Bewertungsfunktion eine schlechte Idee? für Details.

Dies ist die nächstgelegene Option, ohne auf herstellerspezifische Erweiterungen zurückzugreifen:

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

Hilfsfunktion:

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(',');
}

Beweis, dass es in beliebigem Umfang funktioniert:

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

eval wird in Safari nicht unterstützt.

Ninjagecko
quelle
5
+1 für super klug, verwenden Sie dies jedoch nie im wirklichen Leben :-p
Jamund Ferguson
3

Als Aktualisierung der Antwort des Ministers können Sie dies jetzt mit es2015 tun:

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
quelle
Es gibt keinen Grund, warum Sie dies nicht vor ES2015 tun konnten ... Auch dies beantwortet nicht die Frage des OP, er bat um Destrukturierung
ThatWeirdo
3

Sie können auch einen Tupeltyp in Javascript verwenden. Definieren Sie es einfach mit Funktionen höherer Ordnung (der akademische Begriff ist Kirchenkodierung):

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

Bitte beachten Sie, dass dies Tuplenicht als normaler Konstruktor verwendet wird. Die Lösung basiert überhaupt nicht auf dem Prototypsystem, sondern ausschließlich auf Funktionen höherer Ordnung.

Was sind die Vorteile von Tupeln gegenüber ArrayTupeln? Kirchenkodierte Tupel sind vom Design her unveränderlich und verhindern so Nebenwirkungen, die durch Mutationen verursacht werden. Dies hilft, robustere Anwendungen zu erstellen. Darüber hinaus ist es einfacher, über Code nachzudenken, der zwischen Arrays als Sammlungstyp (z. B. [a]) und Tupeln als zugehörige Daten verschiedener Typen (z (a, b). B. ) unterscheidet.


quelle
1

Hier ist eine einfache Implementierung von Javascript Tuple:

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.

Dies ist ein 2-Tupel. Sie können jedoch mein Beispiel so ändern, dass es 3,4,5,6 usw. Tupel unterstützt.

Faris Zacina
quelle
Eigentlich ist dies kein T-uple, sondern ein Paar.
Pier Paolo Ramon
Die tupleInstanz ist immer noch veränderbar, also technisch gesehen kein Tupel. Zum Beweiswechsel tuple.Item1 = "Steve"dann console.log()die Ausgabe.
Evan Plaice
1
Danke, dass du das gefunden hast. Ich habe das Beispiel geändert, um das Tupel unveränderlich zu machen.
Faris Zacina
Wie können Sie dies ändern, um eine beliebige Länge zu unterstützen?
aij
Dies beantwortet nicht die Frage des OP, er bat um Destrukturierung
ThatWeirdo
0

Hier ist eine Version von Matthew James Davis 'Antwort mit den hinzugefügten Python-Tupel-Methoden:

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
quelle