Средство получения набора символов машинописи или реализация доступа, подобного массиву

#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 с этим в своих поисках, но не смог найти способ заставить это работать. Спасибо, что показали мне это!