Призначення змінних JavaScript із кортежів


99

В інших мовах, таких як 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, який слід віддавати перевагу кавовому скрипту для нових проектів.


12
У JavaScript не забудьте оголосити змінні:var tuple, name, age;
Šime Vidas

3
var name=tuple[0], age=tuple[1]; Це трохи більше друку, але потворне може бути завищенням.
Brent Bradburn

Відповіді:


120

У 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
Бергі

64
Зараз він фактично є частиною ES6.
П'єр Паоло Рамон

9
За кілька років моя відповідь стала технічно правильною, але не корисною як для правильної, так і для корисної. Так!
pc1oad1etter

49

Ви повинні зробити це потворно. Якщо ви дійсно хочете щось подібне, ви можете перевірити CoffeeScript , який має це та безліч інших функцій, які роблять його схожим на python (вибачте, що це звучить як реклама, але мені це дуже подобається.)


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

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 .

Але агов, ви можете використовувати transpiler .

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

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

console.log(name)
console.log(age)

2
Майбутнім читачам: ця функція підтримується в chrome починаючи з chrome 49 (згідно з документами Mozilla). Ви також можете перевірити сумісність, використовуючи документи Mozilla тут: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Джеймі

12

Заморожений масив поводиться ідентично кортежу пітона:

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

Сьогодні працює у всіх найновіших браузерах.


Не могли б ви просто встановити для нього const? Конст незмінний, ні?
Марк А

2
Ні. Конст не підлягає перепризначенню. Ви не можете зробити const tuple = ["Jim", 35]; кортеж = ["Джеймс", 35]. Ви можете зробити const tuple = ["Jim", 35]; кортеж [0] = "Джеймс"; Тому він не є незмінним.
Метью Джеймс Девіс,

6

Кортежі не підтримуються в JavaScript

Якщо ви шукаєте незмінний список, Object.freeze () можна використовувати, щоб зробити масив незмінним.

Метод Object.freeze () заморожує об’єкт: тобто перешкоджає додаванню до нього нових властивостей; запобігає видаленню існуючих властивостей; і запобігає зміні існуючих властивостей або їх перелічуваності, конфігурації або запису, По суті, об’єкт робиться фактично незмінним. Метод повертає об'єкт, що заморожений.

Джерело: Mozilla Developer Network - 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 ]

До речі, сподіваємось, Tuples отримає першокласну підтримку в ES6. Справжній природний кортеж (тобто гетерогенна послідовність) також покращить швидкість.
Еван Плейс,

5

На жаль, ви не можете використовувати цей синтаксис присвоєння кортежу в сценарії (ECMA | Java).

РЕДАГУВАТИ: Хтось пов’язаний з 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
Jamund Ferguson

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, що використовуються як кортежі? Закодовані в церкві кортежі незмінні за задумом і таким чином запобігають побічним ефектам, спричиненим мутаціями. Це допомагає створювати більш надійні програми. Крім того, простіше міркувати про код, який розрізняє Arrays як тип колекції (наприклад [a]) та кортежі як пов'язані дані різних типів (наприклад (a, b)).


1

Ось проста реалізація 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.

Це 2-кортеж, однак ви можете змінити мій приклад для підтримки 3,4,5,6 тощо кортежів.


Насправді це не тройка, а пара.
Pier Paolo Ramon

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.