Лучший способ перебрать этот массив для отображения сгруппированных угловых/html-элементов?

#javascript #arrays #angular #multidimensional-array #data-structures

Вопрос:

Я работаю с API, который возвращает варианты оплаты за услугу. Варианты оплаты: (1 год услуги, оплачивается ежемесячно), (1 год услуги, авансом), (2 года услуги, оплачивается ежемесячно), (2 года услуги, авансом). Данные возвращаются следующим образом:

 options = [
  { price: 10,
    serviceLength: "1 year",
    paymentFrequency: "monthly payment",
  },
  { price: 100,
    serviceLength: "1 year",
    paymentFrequency: "one-time payment",
  },
  { price: 8,
    serviceLength: "2 year",
    paymentFrequency: "monthly payment",
  },
  { price: 150,
    serviceLength: "2 year",
    paymentFrequency: "one-time payment",
  },
]
 

Мне нужно отобразить данные таким образом, чтобы объединить параметры обслуживания за 1 год и параметры обслуживания за 2 года вместе. Что-то вроде этого:

Срок Службы 1 Год

  • Ежемесячно — $XX.XX
  • Авансовый платеж: $XX.XX

2 Года Службы

  • Ежемесячно — $XX.XX
  • Авансовый платеж: $XX.XX

Каков наилучший способ организации данных для этого? В будущем, вероятно, будут добавлены дополнительные варианты цен. Я думал о том, чтобы создать функцию для разделения длин года, а затем создать цикл double for для отображения вариантов?

Что-то вроде:

 oneYearOptions = [
  { price: 10,
    serviceLength: "1 year",
    paymentFrequency: "monthly payment",
  },
  { price: 100,
    serviceLength: "1 year",
    paymentFrequency: "one-time payment",
  },

twoYearOptions = [
  { price: 8,
    serviceLength: "2 year",
    paymentFrequency: "monthly payment",
  },
  { price: 150,
    serviceLength: "2 year",
    paymentFrequency: "one-time payment",
  },
];

const allOptions = [oneYearOptions, twoYearOptions];
 

и тогда угловые ts будут чем-то вроде:

 <div *ngFor="let option of allOptions>
   <div *ngFor="let priceOption of priceOptions">
     {{ list the data in here }}
   </div>
</div>
 

Мне это просто кажется неэффективным. Есть ли лучший способ сделать это? Может быть, вместо этого использовать хэш-карту?

Комментарии:

1. Честно говоря, это кажется разумным способом сделать это, учитывая, что у вас нет контроля над форматом возвращаемых данных API. Возможно, вам удастся сэкономить немного памяти, не разделяя массив на несколько массивов, но я не думаю, что в этом действительно есть необходимость. Что касается быстродействия, вам придется зацикливаться на каждом элементе и отображать его независимо, поэтому вы не можете сделать лучше, чем «зацикливаться на каждом элементе».

2. Если вы можете изменить данные, вы можете сделать это в одном цикле, если хотите. У вас может быть массив с объектами, где объекты могут иметь тип «заголовок» или параметры (где параметр-это объект, который у вас есть сейчас).

Ответ №1:

просто объект, имеющий в качестве свойства «параметры» , который является массивом

 options =  
    {serviceLength: "1 year",
     options:[
      { price: 10,
       paymentFrequency: "monthly payment",
      },
      { price: 100,
       paymentFrequency: "one-time payment",
      }]
    },
    { serviceLength: "2 year",
      options:[
      { price: 8,
        paymentFrequency: "monthly payment",
      },
      { price: 150,
        paymentFrequency: "one-time payment",
      }]
    }
]
 

Это правда, что если вы храните данные в базе данных, такой как ваш первый массив объектов, хорошо преобразовать их в другой способ, используя reduce

 this.options=this.data.reduce((a:any,b:any)=>{
    const el=a.find(x=>x.serviceLength==b.serviceLength)
    if (el){
      el.options=[...el.options,{price:b.price,paymentFrequency:b.paymentFrequency}]
      return a;
    }
     return [...a,{serviceLength:b.serviceLength,options:[{price:b.price,paymentFrequency:b.paymentFrequency}]}]
  },[])
 

Файл .html становится похожим

 <div *ngFor="let option of options">
  <div>{{option.serviceLength}}</div>
  <!--see the inner .html loop over "option.options"-->
  <div *ngFor="let priceOption of option.options">
     {{ priceOption.price}} {{priceOption.paymentFrecuency}}
   </div>
</div>
 

Дурак стакблитц

Ответ №2:

Вы также можете перебирать отфильтрованные версии своего массива, если вам не хочется манипулировать данными:

 <div *ngFor="let option of options.filter(option => option.serviceLength == '1 year')">
     {{ list the data in here }}
</div>
<div *ngFor="let option of options.filter(option => option.serviceLength == '2 year')">
     {{ list the data in here }}
</div>
 

Ответ №3:

Я думаю, что мы можем использовать трубу groupBy здесь.

 import { Component, Pipe, PipeTransform } from "@angular/core";

@Pipe({
  name:"groupBy"
})
export class GroupBy implements PipeTransform {
  transform(array: any[]) : number {
    return array.reduce((trasnformedOption, { serviceLength })=>{
      if(!trasnformedOption.some(option => option.serviceLength === serviceLength)){
        trasnformedOption.push({
          serviceLength ,
          serviceDetails : array.filter(v=>v.serviceLength === serviceLength)
        });
      }
      return trasnformedOption;
    },[]);
  }
}

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  options = [
    { price: 10,
      serviceLength: "1 year",
      paymentFrequency: "monthly payment",
    },
    { price: 100,
      serviceLength: "1 year",
      paymentFrequency: "one-time payment",
    },
    { price: 8,
      serviceLength: "2 year",
      paymentFrequency: "monthly payment",
    },
    { price: 150,
      serviceLength: "2 year",
      paymentFrequency: "one-time payment",
    },
  ];
}
 

app.component.html

 <div>
  <div *ngFor="let option of options | groupBy">
    {{ option.serviceLength }}
    <div *ngFor="let details of option.serviceDetails">
      {{ details.paymentFrequency}} - {{ details.price }}
    </div>
  </div>
</div>