Asignaciones de variables de JavaScript desde tuplas

98

En otros lenguajes como Python 2 y Python 3, puede definir y asignar valores a una variable de tupla y recuperar sus valores de la siguiente manera:

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

¿Hay algo similar en JavaScript? O simplemente tengo que hacerlo de la manera más fea con una matriz:

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

¿Existe una mejor manera de simular tuplas de Python en JavaScript 5?

Actualización: consulte la respuesta sobre ES6, que debería favorecerse sobre CoffeeScript para nuevos proyectos.

Karl
fuente
12
En JavaScript, no olvide declarar las variables:var tuple, name, age;
Šime Vidas
3
var name=tuple[0], age=tuple[1]; Es un poco más mecanografiado, pero feo puede ser una exageración.
Brent Bradburn

Respuestas:

119

Javascript 1.7 agregó una asignación desestructurada que le permite hacer esencialmente lo que busca.

function getTuple(){
   return ["Bob", 24];
}
var [a, b] = getTuple();
// a === "bob" , b === 24 are both true
pc1oad1etter
fuente
5
No se trata de un javascript basado en estándares, sino de extensiones específicas de Mozilla.
ninjagecko
14
@ninjagecko: "JavaScript" es la implementación de Mozilla, y las asignaciones de desestructuración serán parte del próximo estándar ecmascript
Bergi
64
De hecho, ahora es parte de ES6.
Pier Paolo Ramon
9
En unos pocos años, mi respuesta se ha vuelto técnicamente correcta, pero no ha sido útil para corregir y ayudar. ¡Hurra!
pc1oad1etter
49

Tienes que hacerlo de la manera más fea. Si realmente quieres algo como esto, puedes consultar CoffeeScript , que tiene eso y muchas otras características que lo hacen parecerse más a Python (lo siento por hacer que suene como un anuncio, pero realmente me gusta).

Gabi Purcaru
fuente
Hmm, esto es bastante interesante, acabo de encontrarme en este escenario, ya que JavaScript no parece proporcionar un soporte de tuplas obvio, y al venir de un período intenso de programación funcional, desarrollas la necesidad de tuplas. También encontré esto, pero no estoy seguro de si funciona, simplemente se veía bien en términos de soporte de tuplas también: cs.umd.edu/projects/PL/arrowlets/api-tuples.xhtml . Definitivamente miraré CoffeeScript.
9codeMan9
29

Puedes hacer algo similar:

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

y luego referirse al nombre y la edad como atributos

tuple.name 
tuple.age 
monika mevenkamp
fuente
5
No votaré negativamente, pero técnicamente esto es incorrecto. Aún puede cambiar (es decir, mutar) los valores de tuple.name y tuple.age después de que se declare el objeto. Por definición, los tipos inmutables no se pueden cambiar una vez definidos. Son como tipos de solo lectura donde tanto los parámetros como sus valores solo se pueden declarar una vez.
Evan Plaice
4
@EvanPlaice si la mutabilidad es un problema, puede usar Object.freeze(), es decir:tuple = Object.freeze({ name:'Bob', age:14 })
canon
@canon Estoy de acuerdo, ese es probablemente el único enfoque aceptable / correcto en todo este hilo. Desafortunadamente, la respuesta de mcm no congela el objeto, por lo que aún es mutable.
Evan Plaice
@EvanPlaice No veo de dónde vino el problema de la mutabilidad: ¡las tuplas no son inmutables en el ejemplo original de Python!
Daniel Buckmaster
@DanielBuckmaster La diferencia entre una lista y una tupla en Python es que una lista es mutable mientras que una tupla no lo es. Consulte docs.python.org/2/tutorial/… . Las tuplas no son compatibles de forma nativa en JavaScript porque todas las estructuras de datos JS son mutables a menos que llame a Object.freeze () en el objeto después de la creación.
Evan Plaice
27

Esta función de "tupla" se denomina desestructuración en EcmaScript2015 y pronto será compatible con los navegadores actualizados. Por el momento, solo Firefox y Chrome lo admiten .

Pero bueno, puedes usar un transpilador .

El código se vería tan bien como Python:

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

console.log(name)
console.log(age)
Adrian
fuente
2
Para futuros lectores: esta función es compatible con Chrome desde Chrome 49 (según los documentos de Mozilla). También puede verificar la compatibilidad usando los documentos de Mozilla aquí: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Jamie
12

Una matriz congelada se comporta de manera idéntica a una tupla de Python:

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

Sea elegante y defina una clase

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

Funciona hoy en todos los navegadores más recientes.

Matthew James Davis
fuente
¿No podrías ponerlo en constante? La constante es inmutable, ¿no?
Mark A
2
No. La constante no se puede reasignar. No puedes hacer const tuple = ["Jim", 35]; tupla = ["James", 35]. Puede hacer const tuple = ["Jim", 35]; tupla [0] = "James"; Por tanto, no es inmutable.
Matthew James Davis
6

Las tuplas no son compatibles con JavaScript

Si está buscando una lista inmutable, Object.freeze () puede usarse para hacer una matriz inmutable.

El método Object.freeze () congela un objeto: es decir, evita que se le agreguen nuevas propiedades; evita que se eliminen las propiedades existentes; e impide que se modifiquen las propiedades existentes, o su enumerabilidad, configurabilidad o capacidad de escritura. En esencia, el objeto se vuelve efectivamente inmutable. El método devuelve el objeto que se congela.

Fuente: Mozilla Developer Network - Object.freeze ()

Asigne una matriz como de costumbre pero ciérrela usando 'Object.freeze ()

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

Use los valores como lo haría con una matriz normal (no se admite la asignación múltiple de Python)

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

Intente asignar un nuevo valor

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

Pero el valor no cambia

> console.log(tuple)
[ 'Bob', 24 ]
Evan Solla
fuente
En una nota al margen, es de esperar que Tuples obtenga un soporte de primera clase en ES6. La verdadera tupla nativa (es decir, secuencia heterogénea) también mejorará la velocidad.
Evan Plaice
5

Desafortunadamente, no puede usar esa sintaxis de asignación de tuplas en el script (ECMA | Java).

EDITAR: Alguien vinculado a Mozilla / JS 1.7: esto no funcionaría en todos los navegadores, pero si no es necesario, entonces está su respuesta.

meder omuraliev
fuente
3

Esto no está destinado a ser utilizado en la vida real, solo es un ejercicio interesante. Consulte ¿Por qué es una mala idea usar la función eval de JavaScript? para detalles.

Esto es lo más cercano que puede obtener sin tener que recurrir a extensiones específicas del proveedor:

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

Función de ayudante:

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

Prueba de que funciona en un ámbito arbitrario:

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

eval no es compatible con Safari.

ninjagecko
fuente
5
+1 por ser súper inteligente, aunque nunca uses esto en la vida real :-p
Jamund Ferguson
3

Como actualización de la respuesta del Ministro, ahora puede hacer esto con 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
fuente
No hay ninguna razón por la que no puedas hacer esto antes de ES2015 ... Además, esto no responde a la pregunta del OP, pidió la desestructuración
ThatWeirdo
3

También puede tener un tipo de tupla en Javascript. Simplemente defínalo con funciones de orden superior (el término académico es codificación de la Iglesia):

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

Tenga en cuenta que Tupleno se utiliza como un constructor normal. La solución no se basa en absoluto en el sistema prototipo, sino únicamente en funciones de orden superior.

¿Cuáles son las ventajas de las tuplas sobre Arraylas que se usan como tuplas? Las tuplas codificadas por Church son inmutables por diseño y, por lo tanto, previenen los efectos secundarios causados ​​por mutaciones. Esto ayuda a crear aplicaciones más sólidas. Además, es más fácil razonar sobre el código que distingue entre Arrays como un tipo de colección (por ejemplo [a]) y tuplas como datos relacionados de varios tipos (por ejemplo (a, b)).


fuente
1

Aquí hay una implementación simple de tupla de 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.

Esta es una tupla de 2, sin embargo, podría modificar mi ejemplo para admitir tuplas 3,4,5,6, etc.

Faris Zacina
fuente
En realidad, esto no es un t-uple sino un par.
Pier Paolo Ramon
La tupleinstancia aún es mutable, por lo que técnicamente no es una tupla. Para el cambio de la prueba tuple.Item1 = "Steve"y luego console.log()la salida.
Evan Plaice
1
Gracias por encontrar eso. He modificado el ejemplo para hacer que Tuple sea inmutable.
Faris Zacina
¿Cómo modifica esto para admitir una longitud arbitraria?
aij
Esto no responde a la pregunta del OP, pidió la desestructuración
ThatWeirdo
0

Aquí hay una versión de la respuesta de Matthew James Davis con los métodos de tupla de Python agregados en:

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)

Nirvana
fuente