Atribuições de variáveis ​​JavaScript de tuplas

98

Em outras linguagens, como Python 2 e Python 3, você pode definir e atribuir valores a uma variável de tupla e recuperar seus valores assim:

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

Existe algo semelhante em JavaScript? Ou eu apenas tenho que fazer isso da maneira feia com um array:

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

Existe uma maneira melhor de simular tuplas Python em JavaScript 5?

Atualização: Veja a resposta sobre ES6, que deve ser preferido em relação ao CoffeeScript para novos projetos.

Karl
fonte
12
Em JavaScript, não se esqueça de declarar as variáveis:var tuple, name, age;
Šime Vidas
3
var name=tuple[0], age=tuple[1]; É um pouco mais de digitação, mas feio pode ser um exagero.
Brent Bradburn

Respostas:

119

Javascript 1.7 adicionou atribuição desestruturada que permite que você faça essencialmente o que você está procurando.

function getTuple(){
   return ["Bob", 24];
}
var [a, b] = getTuple();
// a === "bob" , b === 24 are both true
pc1oad1etter
fonte
5
Isso não é javascript baseado em padrões, mas sim extensões específicas do Mozilla.
ninjagecko
14
@ninjagecko: "JavaScript" é a implementação da Mozilla, e as atribuições de desestruturação farão parte do próximo padrão
ecmascript
64
Agora, na verdade, faz parte do ES6.
Pier Paolo Ramon
9
Após alguns anos, minha resposta tornou-se tecnicamente correta, mas não ajudou tanto a corrigir quanto a ajudar. Yay!
pc1oad1etter
49

Você tem que fazer da maneira mais feia. Se você realmente quer algo assim, pode dar uma olhada no CoffeeScript , que tem isso e muitos outros recursos que o tornam mais parecido com o python (desculpe por fazer parecer um anúncio, mas eu realmente gosto).

Gabi Purcaru
fonte
Hmm, isso é muito interessante, apenas me deparei com este cenário, já que JavaScript não parece fornecer suporte a tuplas óbvio e, vindo de um período intenso de programação funcional, você desenvolve o desejo por tuplas. Eu também encontrei isso, mas não tenho certeza se funciona, apenas parecia bom em termos de suporte de tupla também: cs.umd.edu/projects/PL/arrowlets/api-tuples.xhtml . Definitivamente irei olhar para CoffeeScript.
9codeMan9,
29

Você pode fazer algo semelhante:

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

e então se referem ao nome e idade como atributos

tuple.name 
tuple.age 
Monika Mevenkamp
fonte
5
Não vou votar negativamente, mas tecnicamente isso é incorreto. Você ainda pode alterar (ou seja, modificar) os valores de tuple.name e tuple.age após o objeto ser declarado. Por definição, os tipos imutáveis ​​não podem ser alterados depois de definidos. Eles são como tipos somente leitura, onde os parâmetros e seus valores só podem ser declarados uma vez.
Evan Plaice
4
@EvanPlaice se a mutabilidade for um problema, você pode usar Object.freeze(), por exemplo:tuple = Object.freeze({ name:'Bob', age:14 })
canon
@canon Eu concordo, essa é provavelmente a única abordagem aceitável / correta em todo este tópico. Infelizmente, a resposta do mcm não congela o objeto, então ele ainda é mutável.
Evan Plaice
@EvanPlaice Não vejo de onde veio o problema da mutabilidade - tuplas não são imutáveis ​​no exemplo original do Python!
Daniel Buckmaster
@DanielBuckmaster A diferença entre uma lista e uma tupla em Python é que uma lista é mutável, enquanto uma tupla não. Consulte docs.python.org/2/tutorial/… . As tuplas não são nativamente suportadas em JavaScript porque todas as estruturas de dados JS são mutáveis, a menos que você chame Object.freeze () no objeto após a criação.
Evan Plaice
27

Esse recurso de "tupla" é chamado de desestruturação no EcmaScript2015 e em breve será compatível com navegadores atualizados. Por enquanto, apenas Firefox e Chrome suportam .

Mas hey, você pode usar um transpiler .

O código seria tão bom quanto python:

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

console.log(name)
console.log(age)
Adrian
fonte
2
Para futuros leitores: este recurso é compatível com o Chrome desde o Chrome 49 (de acordo com os documentos do Mozilla). Você também pode verificar a compatibilidade usando os documentos do Mozilla aqui: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Jamie
12

Uma matriz congelada se comporta de forma idêntica a uma tupla Python:

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

Seja sofisticado e defina uma classe

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 hoje em todos os navegadores mais recentes.

Matthew James Davis
fonte
Você não poderia simplesmente defini-lo como um const? A const é imutável, não?
Mark A
2
Não. A const não pode ser reatribuída. Você não pode fazer const tuple = ["Jim", 35]; tupla = ["James", 35]. Você pode fazer const tuple = ["Jim", 35]; tupla [0] = "James"; Portanto, não é imutável.
Matthew James Davis
6

Tuplas não são compatíveis com JavaScript

Se você estiver procurando por uma lista imutável, Object.freeze () pode ser usado para tornar um array imutável.

O método Object.freeze () congela um objeto: isto é, evita que novas propriedades sejam adicionadas a ele; impede que as propriedades existentes sejam removidas; e impede que as propriedades existentes, ou sua enumerabilidade, configurabilidade ou capacidade de gravação, sejam alteradas. Em essência, o objeto é tornado efetivamente imutável. O método retorna o objeto que está sendo congelado.

Fonte: Mozilla Developer Network - Object.freeze ()

Atribua uma matriz como de costume, mas bloqueie-a usando 'Object.freeze ()

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

Use os valores como faria com uma matriz regular (a multi-atribuição de python não é compatível)

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

Tentar atribuir um novo valor

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

Mas o valor não é alterado

> console.log(tuple)
[ 'Bob', 24 ]
Evan Solha
fonte
Em uma observação lateral, esperançosamente, Tuplas obterão suporte de primeira classe no ES6. A tupla nativa verdadeira (ou seja, sequência heterogênea) também melhora a velocidade.
Evan Plaice
5

Infelizmente, você não pode usar essa sintaxe de atribuição de tupla no (ECMA | Java) Script.

EDIT: Alguém vinculado ao Mozilla / JS 1.7 - isso não funcionaria em vários navegadores, mas se não for necessário, aí está sua resposta.

Meder Omuraliev
fonte
3

Isso não se destina a ser usado na vida real, apenas um exercício interessante. Veja Por que usar a função eval JavaScript é uma má ideia? para detalhes.

Este é o mais próximo que você pode obter sem recorrer a extensões específicas do fornecedor:

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

Função auxiliar:

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

Prova de que funciona em âmbito arbitrário:

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

eval não é compatível com Safari.

ninjagecko
fonte
5
+1 por ser superinteligente, mas nunca use isso na vida real :-p
Jamund Ferguson
3

Para atualizar a resposta do Ministro, agora você pode fazer isso com 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
fonte
Não há razão para que você não pudesse fazer isso antes de ES2015 ... Também isso não responde a pergunta do OP, ele pediu para desestruturar
ThatWeirdo
3

Você também pode ter um tipo de tupla em Javascript. Basta defini-lo com funções de ordem superior (o termo acadêmico é codificação de Igreja):

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

Observe que Tuplenão é usado como um construtor normal. A solução não depende do sistema de protótipo, mas apenas de funções de ordem superior.

Quais são as vantagens das tuplas sobre Arrayas usadas como tuplas? As tuplas codificadas por igreja são imutáveis ​​por design e, portanto, evitam os efeitos colaterais causados ​​por mutações. Isso ajuda a construir aplicativos mais robustos. Além disso, é mais fácil raciocinar sobre o código que distingue entre Arrays como um tipo de coleção (por exemplo [a]) e tuplas como dados relacionados de vários tipos (por exemplo (a, b)).


fonte
1

Aqui está uma implementação simples de Tuple 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 é uma tupla de 2, no entanto, você pode modificar meu exemplo para suportar tuplas 3,4,5,6 etc.

Faris Zacina
fonte
Na verdade, este não é um t-uple, mas um par.
Pier Paolo Ramon
A tupleinstância ainda é mutável, portanto, tecnicamente, não é uma tupla. Para alteração de prova, tuple.Item1 = "Steve"então console.log()a saída.
Evan Plaice
1
Obrigado por encontrar isso. Modifiquei o exemplo para tornar a Tupla imutável.
Faris Zacina
Como você modifica isso para oferecer suporte a um comprimento arbitrário?
aij
Isso não responde à pergunta do OP, ele pediu a desestruturação
ThatWeirdo
0

Aqui está uma versão da resposta de Matthew James Davis com os métodos de tupla Python adicionados em:

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
fonte