Назначение переменных 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

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)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.