Назначение переменных JavaScript из кортежей

102

В других языках, таких как Python 2 и Python 3, вы можете определять и присваивать значения переменной кортежа и получать их значения следующим образом:

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

Есть ли что-нибудь подобное в JavaScript? Или мне просто нужно сделать это некрасиво с массивом:

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

Есть ли лучший способ имитировать кортежи Python в JavaScript 5?

Обновление: см. Ответ относительно ES6, которому следует отдавать предпочтение перед CoffeeScript для новых проектов.

Карл
источник
12
В JavaScript не забудьте объявить переменные:var tuple, name, age;
Шиме Видас,
3
var name=tuple[0], age=tuple[1]; Это немного больше печатать, но некрасиво может быть преувеличением.
Brent Bradburn

Ответы:

122

В Javascript 1.7 добавлено деструктурированное назначение, которое позволяет вам делать то, что вам нужно.

function getTuple(){
   return ["Bob", 24];
}
var [a, b] = getTuple();
// a === "bob" , b === 24 are both true
pc1oad1etter
источник
5
Это не основанный на стандартах javascript, а скорее специфичные для Mozilla расширения.
ninjagecko
14
@ninjagecko: «JavaScript» - это реализация Mozilla, и назначение деструктурирования станет частью будущего стандарта ecmascript
Берги
66
Теперь это фактически часть ES6.
Пьер Паоло Рамон
10
Спустя несколько лет мой ответ стал технически правильным, но не помог ни правильным, ни полезным. Ура!
pc1oad1etter
49

Вы должны делать это некрасиво. Если вам действительно нужно что-то подобное, вы можете проверить CoffeeScript , в котором есть это и множество других функций, которые делают его больше похожим на python (извините, что это звучит как реклама, но мне это очень нравится).

Габи Пуркару
источник
Хм, это довольно интересно, я только что столкнулся с этим сценарием сам, поскольку JavaScript, похоже, не обеспечивает очевидной поддержки кортежей, и, исходя из интенсивного периода функционального программирования, у вас возникает потребность в кортежах. Я также только что нашел это, но не уверен, работает ли он, просто он выглядел хорошо с точки зрения поддержки кортежей: cs.umd.edu/projects/PL/arrowlets/api-tuples.xhtml . Я обязательно посмотрю CoffeeScript.
9codeMan9 05
29

Вы можете сделать что-то подобное:

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

а затем назовите имя и возраст атрибутами

tuple.name 
tuple.age 
Моника Мевенкамп
источник
5
Я не буду голосовать против, но технически это неверно. Вы по-прежнему можете изменять (т.е. видоизменять) значения tuple.name и tuple.age после объявления объекта. По определению неизменяемые типы не могут быть изменены после их определения. Они похожи на типы, доступные только для чтения, где и параметры, и их значения могут быть объявлены только один раз.
Эван Плейс
4
@EvanPlaice, если изменчивость является проблемой, вы можете использовать Object.freeze(), например:tuple = Object.freeze({ name:'Bob', age:14 })
canon
@canon Я согласен, это, вероятно, единственный приемлемый / правильный подход во всей этой теме. К сожалению, ответ mcm не замораживает объект, поэтому он все еще изменяемый.
Эван Плэйс
@EvanPlaice Я не понимаю, откуда взялась проблема изменчивости - кортежи не являются неизменяемыми в исходном примере Python!
Дэниел Бакмастер,
@DanielBuckmaster Разница между списком и кортежем в Python в том, что список изменяемый, а кортеж - нет. См. Docs.python.org/2/tutorial/… . Кортежи изначально не поддерживаются в JavaScript, потому что все структуры данных JS изменяемы, если вы не вызываете Object.freeze () для объекта после создания.
Эван Плэйс,
27

Эта функция "кортежа" называется деструктуризацией в EcmaScript2015 и скоро будет поддерживаться современными браузерами. На данный момент его поддерживают только Firefox и Chrome .

Но послушайте, вы можете использовать транспилер .

Код выглядел бы так же красиво, как python:

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

console.log(name)
console.log(age)
Адриан
источник
2
Будущим читателям: эта функция поддерживается в Chrome с версии 49 (согласно документации Mozilla). Вы также можете проверить совместимость с помощью документации Mozilla здесь: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Джейми,
13

Замороженный массив ведет себя идентично кортежу python:

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

Будьте фантазией и определите класс

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

Сегодня работает во всех последних браузерах.

Мэтью Джеймс Дэвис
источник
Не могли бы вы просто установить его на константу? Константа неизменна, не так ли?
Mark A
3
Нет. Константу нельзя переназначить. Вы не можете использовать const tuple = ["Jim", 35]; tuple = ["Джеймс", 35]. Вы можете использовать const tuple = ["Jim", 35]; кортеж [0] = "Джеймс"; Следовательно, он не неизменен.
Мэтью Джеймс Дэвис
6

Кортежи не поддерживаются в JavaScript

Если вы ищете неизменяемый список, можно использовать Object.freeze (), чтобы сделать массив неизменяемым.

Метод Object.freeze () замораживает объект: то есть предотвращает добавление к нему новых свойств; предотвращает удаление существующих свойств; и предотвращает изменение существующих свойств или их перечислимости, возможности настройки или записи. По сути, объект сделан фактически неизменяемым. Метод возвращает замораживаемый объект.

Источник: Сеть разработчиков Mozilla - Object.freeze ()

Назначьте массив как обычно, но заблокируйте его с помощью Object.freeze ()

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

Используйте значения, как обычный массив (множественное присвоение python не поддерживается)

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

Попытка присвоить новое значение

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

Но стоимость не изменилась

> console.log(tuple)
[ 'Bob', 24 ]
Эван Плэйс
источник
Кстати, надеюсь, кортежи получат первоклассную поддержку в ES6. Истинный собственный кортеж (т.е. гетерогенная последовательность) также улучшит скорость.
Эван Плейс,
5

К сожалению, вы не можете использовать этот синтаксис назначения кортежей в (ECMA | Java) Script.

РЕДАКТИРОВАТЬ: кто-то связан с Mozilla / JS 1.7 - это не будет работать в кросс-браузере, но если это не требуется, то вот ваш ответ.

Медер Омуралиев
источник
3

Это не предназначено для использования в реальной жизни, это просто интересное упражнение. Видеть Почему использование функции eval в JavaScript - плохая идея? для подробностей.

Это самое близкое, что вы можете получить, не прибегая к расширению, зависящему от производителя:

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

Вспомогательная функция:

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

Доказательство того, что он работает в произвольном объеме:

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

eval не поддерживается в Safari.

ниндзягеко
источник
5
+1 за то, что он супер умный, никогда не используйте это в реальной жизни :-p
Джамунд Фергюсон
3

В качестве обновления ответа министра теперь вы можете сделать это с помощью 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

Зак Даль
источник
Нет причин, по которым вы не могли бы сделать это до ES2015 ... Также это не отвечает на вопрос ОП, он попросил деструктурировать
ThatWeirdo
3

У вас также может быть тип кортежа в Javascript. Просто определите его с помощью функций высшего порядка (академический термин - кодировка Чёрча):

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

Обратите внимание, что Tupleон не используется как обычный конструктор. Решение вообще не полагается на систему-прототип, а исключительно на функции более высокого порядка.

Каковы преимущества кортежей перед Arrays, используемыми как кортежи? Кортежи в кодировке Church неизменяемы по замыслу и, таким образом, предотвращают побочные эффекты, вызванные мутациями. Это помогает создавать более надежные приложения. Кроме того, легче рассуждать о коде, который различает Arrays как тип коллекции (например [a]) и кортежи как связанные данные различных типов (например (a, b)).


источник
1

Вот простая реализация кортежа 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.

Это 2-кортеж, однако вы можете изменить мой пример для поддержки 3,4,5,6 и т. Д. Кортежей.

Фарис Засина
источник
На самом деле это не тройка, а пара.
Пьер Паоло Рамон
tupleЭкземпляр остается изменчивым, так что это технически не кортеж. Для пруфа поменяйте tuple.Item1 = "Steve"потом console.log()вывод.
Эван Плейс,
1
Спасибо, что нашли это. Я изменил пример, чтобы сделать Tuple неизменяемым.
Фарис Засина,
Как вы измените это, чтобы поддерживать произвольную длину?
aij
Это не ответ на вопрос ОП, он попросил деструктурировать
ThatWeirdo
0

Вот версия ответа Мэтью Джеймса Дэвиса с добавленными методами кортежа 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)

Нирвана
источник