Переменные JavaScript-назначения из кортежей
В других языках, таких как 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?
12 ответов
Вы должны сделать это безобразно. Если вы действительно хотите что-то подобное, вы можете проверить CoffeeScript, который обладает этим и множеством других функций, которые делают его похожим на python (извините за то, что он звучит как реклама, но мне очень нравится).
Javascript 1.7 добавил деструктурированное назначение, которое позволяет вам делать то, что вы хотите.
function getTuple(){
return ["Bob", 24];
}
var [a, b] = getTuple();
// a === "bob" , b === 24 are both true
Вы можете сделать что-то подобное:
var tuple = Object.freeze({ name:'Bob', age:14 })
а затем обратитесь к имени и возрасту в качестве атрибутов
tuple.name
tuple.age
Эта "кортежная" функция в EcmaScript2015 называется деструктуризацией и скоро будет поддерживаться современными браузерами. Пока что только Firefox и Chrome поддерживают его.
Но вы можете использовать транспортер.
Код будет выглядеть так же хорошо, как Python:
let tuple = ["Bob", 24]
let [name, age] = tuple
console.log(name)
console.log(age)
Замороженный массив ведет себя идентично кортежу 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
Работает сегодня во всех последних браузерах.
Кортежи не поддерживаются в 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 ]
К сожалению, вы не можете использовать этот синтаксис присваивания кортежей в (ECMA|Java)Script.
РЕДАКТИРОВАТЬ: Кто-то связался с Mozilla/JS 1.7 - это не будет работать кросс-браузер, но если это не требуется, то есть ваш ответ.
В качестве обновления к ответу министра, теперь вы можете сделать это с 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"
Это не предназначено для реального использования в реальной жизни, просто интересное упражнение. См. Почему использование JavaScript-функции eval - плохая идея? для деталей.
Это самое близкое, что вы можете получить, не прибегая к конкретным расширениям:
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.
Вы также можете иметь тип кортежа в 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
не используется в качестве обычного конструктора. Решение вовсе не опирается на систему-прототип, а исключительно на функции более высокого порядка.
Каковы преимущества кортежей над Array
используется как кортежи? Закодированные церковью кортежи являются неизменными по своему дизайну и, таким образом, предотвращают побочные эффекты, вызванные мутациями. Это помогает создавать более надежные приложения. Кроме того, легче рассуждать о коде, который различает Array
s как тип коллекции (например, [a]
) и кортежи как связанные данные различных типов (например, (a, b)
).
Я сделал реализацию кортежа, которая работает довольно хорошо. Это решение позволяет выполнять деструктуризацию массива, а также базовую проверку типов.
const Tuple = (function() {
function Tuple() {
// Tuple needs at least one element
if (arguments.length < 1) {
throw new Error('Tuple needs at least one element');
}
const args = { ...arguments };
// Define a length property (equal to the number of arguments provided)
Object.defineProperty(this, 'length', {
value: arguments.length,
writable: false
});
// Assign values to enumerable properties
for (let i in args) {
Object.defineProperty(this, i, {
enumerable: true,
get() {
return args[+i];
},
// Checking if the type of the provided value matches that of the existing value
set(value) {
if (typeof value !== typeof args[+i]) {
throw new Error('Cannot assign ' + typeof value + ' on ' + typeof args[+i]);
}
args[+i] = value;
}
});
}
// Implementing iteration with Symbol.iterator (allows for array destructuring as well for...of loops)
this[Symbol.iterator] = function() {
const tuple = this;
return {
current: 0,
last: tuple.length - 1,
next() {
if (this.current <= this.last) {
let val = { done: false, value: tuple[this.current] };
this.current++;
return val;
} else {
return { done: true };
}
}
};
};
// Sealing the object to make sure no more values can be added to tuple
Object.seal(this);
}
// check if provided object is a tuple
Tuple.isTuple = function(obj) {
return obj instanceof Tuple;
};
// Misc. for making the tuple more readable when printing to the console
Tuple.prototype.toString = function() {
const copyThis = { ...this };
const values = Object.values(copyThis);
return `(${values.join(', ')})`;
};
// conctat two instances of Tuple
Tuple.concat = function(obj1, obj2) {
if (!Tuple.isTuple(obj1) || !Tuple.isTuple(obj2)) {
throw new Error('Cannot concat Tuple with ' + typeof (obj1 || obj2));
}
const obj1Copy = { ...obj1 };
const obj2Copy = { ...obj2 };
const obj1Items = Object.values(obj1Copy);
const obj2Items = Object.values(obj2Copy);
return new Tuple(...obj1Items, ...obj2Items);
};
return Tuple;
})();
const SNAKE_COLOR = new Tuple(0, 220, 10);
const [red, green, blue] = SNAKE_COLOR;
console.log(green); // => 220
Вот простая реализация Javascript Tuple:
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 и т. Д.
Вот версия ответа Мэтью Джеймса Дэвиса с добавленными методами кортежа 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)