Search code examples
arraysangularngforarray-filter

Angular *ngFor: Display only unique property "category" value and lowest price in each "category"


I have an array with objects having "category" & "price" properties among others, in the Angular application I need to display only unique values of "category" property (Example: Economy, Premium & Deluxe) along with the lowest price in that category. I tried filtering it but was unable to achieve it. So can you please help how this can be achieved in Angular? Thank you.

In this example, I need to show:

Economy - Starts from 250USD

Premium - Starts from 400USD

Deluxe - Starts from 600USD

"hotelRooms": [
{
    "price": {
        "total": 250,
        "currency": "USD"
    },
    "category": "Economy",
    "subcategory": "single",
},
{
    "price": {
        "total": 350,
        "currency": "USD"
    },
    "category": "Economy",
    "subcategory": "double",
},
{
    "price": {
        "total": 450,
        "currency": "USD"
    },
    "category": "Economy",
    "subcategory": "family",
},
{
    "price": {
        "total": 400,
        "currency": "USD"
    },
    "category": "Premium",
    "subcategory": "single",
},
{
    "price": {
        "total": 500,
        "currency": "USD"
    },
    "category": "Premium",
    "subcategory": "double",
},
{
    "price": {
        "total": 600,
        "currency": "USD"
    },
    "category": "Deluxe",
    "subcategory": "single",
},
{
    "price": {
       "total": 700,
       "currency": "USD"
    },
    "category": "Deluxe",
    "subcategory": "double",
}
]

And in Angular:

<div *ngFor="let room of hotelRooms">
    <span class="bold">{{ room.category }}</span> - Starts from {{ room.price.total}}{{ room.price.currency}}
</div>

Solution

  • From what I understand on this question, you need to group by category and next get the lowest price.amount for each category.

    Concept (TL;DR)

    1. Group By Category

      1.1 Create an array for each category if the category array does not exist. Else reuse the created category array.

      1.2 From each category array, find the lowest record based on price.amount.

      1.3 If the result (from 1.2) return no value (undefined), first reset category array to empty array and add the current record to the category array. This ensures that each category will only have 1 record with the lowest price. Else just ignore it.

    2. Data Transformation

      2.1 Get the item from each category via iterate with key. It will return arrays.

      2.2 Concat multiple arrays (from 2.1) into one array.

    let groupByCategories = [];
    
    groupByCategories = this.hotelRooms.reduce(function (previous, current) {
      previous[current.category] = previous[current.category] || [];
    
      let lowest = previous[current.category].find(
        (x) =>
          x.price.total < current.price.total
      );
    
      if (!lowest) {
        previous[current.category] = [];
        previous[current.category].push(current);
      }
    
      return previous;
    }, Object.create(null));
    
    this.hotelRooms = [].concat(
      ...Object.keys(groupByCategories).map((key) => {
        return groupByCategories[key];
      })
    );
    

    Sample Demo on StackBlitz