1

I need to write a custom pipe in Angular that takes an array of objects and a variable called order with a value of either ascending or descending as a parameter and then sort the array of objects by a property value.

The data looks something like this:

[
    {
        "location_type": "KAUPPA",
        "postalcode": "100",
        "availability": "LIIKETILAN AUKIOLO",
        "location": "SUOMALAINEN KIRJAKAUPPA / SISÄKÄYTÄVÄ",
        "municipality": "TURKU",
        "target_address": "ALEKSANTERINKATU 23",
        "availability_details": "",
        "coordinates_lon": "24.941095",
        "coordinates_lat": "60.168718"
    },
    {
        "location_type": "PANKIN KONTTORI",
        "postalcode": "100",
        "availability": "ITSEPALVELUALUEEN AUKIOLO",
        "location": "NORDEA SENAATINTORI",
        "municipality": "VANTAA",
        "target_address": "ALEKSANTERINKATU 30",
        "availability_details": "ma-su klo 06-22",
        "coordinates_lon": "24.950720",
        "coordinates_lat": "60.168930"
    },
    {
        "location_type": "TAVARATALO",
        "postalcode": "100",
        "availability": "LIIKETILAN AUKIOLO",
        "location": "STOCKMANN / 8. KERROS",
        "municipality": "HELSINKI",
        "target_address": "ALEKSANTERINKATU 52",
        "availability_details": "",
        "coordinates_lon": "24.941870",
        "coordinates_lat": "60.168430"
    }
]

The objects in the array need to be put into an order by municipality's value.

6
  • 3
    You generally don't use pipes to do sorting angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe Commented Feb 12, 2019 at 11:36
  • Can I sort the data using a custom pipe at all? Or is there a better alternative? Commented Feb 12, 2019 at 11:51
  • Yes there is. It's well explained at the link I posted Commented Feb 12, 2019 at 11:52
  • But I dont see any cons of using pipe in such scenerios @bambam, may be I was wrong Commented Feb 12, 2019 at 12:01
  • 1
    Just read the link I've posted. Sorting is a expensive operation, and will be run multiple times per second if used as a pipe @PardeepJain Commented Feb 12, 2019 at 12:02

3 Answers 3

3

As mentioned several times in the Comments and on the above answer by Pardeep as well, using Pipe to sort the data is not a very good idea.

If you want to sort fields, just implement it on your template and then trigger the sort function only on events. This would significantly save you performance.

Here, give this a try:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  lastSortedByField;
  ascendingOrder = true;
  data = [
    {
      "location_type": "KAUPPA",
      "postalcode": "100",
      "availability": "LIIKETILAN AUKIOLO",
      "location": "SUOMALAINEN KIRJAKAUPPA / SISÄKÄYTÄVÄ",
      "municipality": "TURKU",
      "target_address": "ALEKSANTERINKATU 23",
      "availability_details": "",
      "coordinates_lon": "24.941095",
      "coordinates_lat": "60.168718"
    },
    {
      "location_type": "PANKIN KONTTORI",
      "postalcode": "100",
      "availability": "ITSEPALVELUALUEEN AUKIOLO",
      "location": "NORDEA SENAATINTORI",
      "municipality": "VANTAA",
      "target_address": "ALEKSANTERINKATU 30",
      "availability_details": "ma-su klo 06-22",
      "coordinates_lon": "24.950720",
      "coordinates_lat": "60.168930"
    },
    {
      "location_type": "TAVARATALO",
      "postalcode": "100",
      "availability": "LIIKETILAN AUKIOLO",
      "location": "STOCKMANN / 8. KERROS",
      "municipality": "HELSINKI",
      "target_address": "ALEKSANTERINKATU 52",
      "availability_details": "",
      "coordinates_lon": "24.941870",
      "coordinates_lat": "60.168430"
    }
  ];

  sortByField(field) {
    if(this.lastSortedByField === field) {
      this.ascendingOrder = !this.ascendingOrder;
    }
    else {
      this.lastSortedByField = field;
      this.ascendingOrder = true;
    }

    if(this.ascendingOrder) {
      this.data = this.data.sort((a, b) => {
        if (a[field] < b[field])
          return -1;
        if (a[field] > b[field])
          return 1;
        return 0;
      });
    } else {
      this.data = this.data.sort((a, b) => {
        if (a[field] < b[field])
          return 1;
        if (a[field] > b[field])
          return -1;
        return 0;
      });
    }

  }

}

And in the Template:

<table border="1">
  <thead>
    <tr>
      <td (click)="sortByField('location_type')">location_type</td>
      <td (click)="sortByField('postalcode')">postalcode</td>
      <td (click)="sortByField('availability')">availability</td>
      <td (click)="sortByField('location')">location</td>
      <td (click)="sortByField('municipality')">municipality</td>
      <td (click)="sortByField('target_address')">target_address</td>
      <td (click)="sortByField('availability_details')">availability_details</td>
      <td (click)="sortByField('coordinates_lon')">coordinates_lon</td>
      <td (click)="sortByField('coordinates_lat')">coordinates_lat</td>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let datum of data">
      <td>{{ datum.location_type }}</td>
      <td>{{ datum.postalcode }}</td>
      <td>{{ datum.availability }}</td>
      <td>{{ datum.location }}</td>
      <td>{{ datum.municipality }}</td>
      <td>{{ datum.target_address }}</td>
      <td>{{ datum.availability_details }}</td>
      <td>{{ datum.coordinates_lon }}</td>
      <td>{{ datum.coordinates_lat }}</td>
    </tr>
  </tbody>
</table>

Here's a Working Sample StackBlitz for your ref.

Sign up to request clarification or add additional context in comments.

Comments

1

Official Docs - Filtering and especially sorting are expensive operations.

The Angular team and many experienced Angular developers strongly recommend moving filtering and sorting logic into the component itself.

In general no need to use custom pipe for such case instead you can directly sort your data into your component class. But if you want to go with usage of pipe, refer below -

@Pipe({name: 'sorted'})
export class SortedPipe implements PipeTransform {
  transform(value: any){
    const data =  value.sort((a,b) => a.municipality.localeCompare(b.municipality));
    return data;
  } 
}

Working example

3 Comments

What's wrong? why downvote, instead comment it down so that at least I could know why my answer is getting downvotes.
He wants to sort either ascending or descending. A pipe should also be reusable, so you would typically pass the property you want to access to it instead of hardcoding it.
true , agree to you point @bambam anything else I can improve in this answer?
0

Typesafe solution using ramda:

import {Pipe, PipeTransform} from '@angular/core';
import {prop, sortBy} from 'ramda';

@Pipe({name: 'sortBy', standalone: true})
export class SortByPipe implements PipeTransform {
  public transform<T>(value: Array<T>, sortByKey: keyof T): Array<T> {
    return sortBy(prop(sortByKey))(value);
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.