#arrays #typescript
Вопрос:
Обновление: Решение
Я немного адаптировал принятое ниже решение, чтобы оно соответствовало моему варианту использования. Все кредиты на плакат принятого решения.
export class MyClass {
//Declare we'll be using arbitrary number accessors
[int: number]: any;
list: any[] = [
{ name: 'apples', amount: 3, price: 10 },
{ name: 'bananas', amount: 1, price: 3 },
{ name: 'mangos', amount: 5, price: 18 },
];
constructor() {
//On instantiation, create the getters.
this.createProperties()
}
private createProperties() {
this.list.map((c, i) => {
Object.defineProperty(this, i, {
configurable: true,
get: () => {
const v = this.getItemWithPrice(i);
//Cleverly replace this getter with the value of the calculation.
//Keeps us from redoing the calculations when the same property is gotten again.
Object.defineProperty(this.list, i, { value: v });
return v
},
});
});
}
getItemWithPrice(index: number) {
//Some calculation before returning the item.
if (!this.list[index].total) this.list[index].total = this.list[index].amount * this.list[index].price;
return this.list[index];
}
}
const someList = new MyClass();
//I can now access the list like so:
someList[1]; // { name: 'bananas', amount: 1, price: 3, total: 3 },
Оригинальное сообщение
Я пытаюсь получить доступ к элементам в списке, значения которых будут вычислены после запроса. Рассмотрим пример:
export class MyClass {
list = [
{ name: 'apples', amount: 3, price: 10 },
{ name: 'bananas', amount: 1, price: 3 },
{ name: 'mangos', amount: 5, price: 18 },
];
getItemWithPrice(index: number) {
//Some calculation before returning the item.
if (!this.list[index].total) this.list[index].total = this.list[index].amount * this.list[index].price;
return this.list[index];
}
}
const someList = new MyClass();
Я хотел бы получить доступ к этому в виде массива , например: someList[1];
, который вернул бы значение someList.getItemWithPrice(1);
.
В моем случае вычисления сложны и могут содержать тысячи элементов, поэтому я не хочу тратить ресурсы на выполнение вычислений, которые нам не нужны. (В противном случае я мог бы просто заранее рассчитать цены и вернуть массив с результатами.)
Я искал способы либо расширить массив, либо реализовать итератор, либо даже использовать прокси-сервер, но, похоже, я не могу заставить это работать. Я могу заставить прокси работать, но это кажется немного банальным, и я теряю имя конструктора моего исходного класса. (Прокси, вероятно, неплохой запасной вариант, но, похоже, это должно быть проще, зная другие языки, в которых есть магические методы, или бесчисленные интерфейсы и т. Д.)
Ответ №1:
Хотя прокси-серверы могут работать, они медленные, странные и не могут быть заполнены для устаревших браузеров. На вашем месте я бы вместо этого создал объект с помощью геттеров, повторяя числовые обозначения исходного массива.
const listData = [
{ name: 'apples', amount: 3, price: 10 },
{ name: 'bananas', amount: 1, price: 3 },
{ name: 'mangos', amount: 5, price: 18 },
];
const someList = Object.defineProperties({}, Object.fromEntries(
listData.map((item, i) => [i, {
configurable: true,
get() {
console.log('Performing calculations');
// Some calculation before returning the item.
item.total = item.amount * item.price;
// Set the property directly on the someList object, removing this getter
Object.defineProperty(someList, i, { value: item });
return item;
}
}])
));
console.log(someList[1]);
console.log(someList[2]);
console.log(someList[1]);
В TS вам нужно будет объявить объекты как, возможно, обладающие total
свойством.
const listData: Array<{ name: string, amount: number, price: number, total?: number }> = [
Комментарии:
1. Я не знал, что могу динамически определять такие функции получения! Я столкнулся
Object.defineProperties
с этим в своих поисках, но не смог найти способ заставить это работать. Спасибо, что показали мне это!