user7209780
user7209780

Reputation:

Sort an array of objects in Angular2

I have problems with sorting an array of object in Angular2.

The object looks like:

[
  {
    "name": "t10",
    "ts": 1476778297100,
    "value": "32.339264",
    "xid": "DP_049908"
  },
  {
    "name": "t17",
    "ts": 1476778341100,
    "value": "true",
    "xid": "DP_693259"
  },
  {
    "name": "t16",
    "ts": 1476778341100,
    "value": "true",
    "xid": "DP_891890"
  }
]

And is being stored inside the values variable.

All I want is to make the *ngFor loop sort it by the name property.

<table *ngIf="values.length">
    <tr *ngFor="let elem of values">
      <td>{{ elem.name }}</td>
      <td>{{ elem.ts }}</td>
      <td>{{ elem.value }}</td>
    </tr>
</table>

Tried to do it with pipes, but failed miserably. Any help appreciated.

Plunker link: https://plnkr.co/edit/e9laTBnqJKb8VzhHEBmn?p=preview

Edit

My pipe:

import {Component, Inject, OnInit, Pipe, PipeTransform} from '@angular/core';

@Component({
  selector: 'watchlist',
  templateUrl: './watchlist.component.html',
  styleUrls: ['./watchlist.component.css'],
  pipes: [ ArraySortPipe ]
})
@Pipe({
  name: "sort"
})

export class ArraySortPipe implements PipeTransform {
  transform(array: Array<string>, args: string): Array<string> {
    array.sort((a: any, b: any) => {
      if (a < b) {
        return -1;
      } else if (a > b) {
        return 1;
      } else {
        return 0;
      }
    });
    return array;
  }
}

And just put the pipe name into html file:

<tr *ngFor="let elem of values | sort">

Upvotes: 26

Views: 86889

Answers (6)

Vikas Gautam
Vikas Gautam

Reputation: 1923

This is adaptable to any such usecase.

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

@Pipe({
  name: 'sortBy'
})
export class SortByPipe implements PipeTransform {
  transform(arr: Array<any>, prop: any, reverse: boolean = false): any {
    if (arr === undefined) return
    const m = reverse ? -1 : 1
    return arr.sort((a: any, b: any): number => {
      const x = a[prop]
      const y = b[prop]
      return (x === y) ? 0 : (x < y) ? -1*m : 1*m
    })
  }
}

Usage:-
<div *ngFor="let item of list | sortBy: 'isDir': true">

UPDATE
Refer Bo's answer as filtering and sorting in pipes is not recommended.

Upvotes: 3

suphi
suphi

Reputation: 259

Try this

Sort from A to end of alpahbet:

this.suppliers.sort((a,b)=>a.SupplierName.localeCompare(b.SupplierName));

Z=>A (reverse order)

this.suppliers.sort((a,b)=>b.SupplierName.localeCompare(a.SupplierName));

Upvotes: 24

Andres Pirona
Andres Pirona

Reputation: 1

its simple, for example if you have this array object:

 cars = ["Dodge", "Fiat", "Audi", "Volvo", "BMW", "Ford"];

and you want to have the object sorted in the HTML front, use:

 <li ng-repeat="x in cars | orderBy">{{x}}</li>

By default, strings are sorted alphabetically, and numbers are sorted numerically.

If you have this array with keys:

customers = [
{"name" : "Bottom-Dollar Marketse" ,"city" : "Tsawassen"},
{"name" : "Alfreds Futterkiste", "city" : "Berlin"},
{"name" : "Bon app", "city" : "Marseille"},
{"name" : "Cactus Comidas para llevar", "city" : "Buenos Aires"},
{"name" : "Bolido Comidas preparadas", "city" : "Madrid"},
{"name" : "Around the Horn", "city" : "London"},
{"name" : "B's Beverages", "city" : "London"}
];

Use:

Sort the array by "city" DESC order:

<li ng-repeat="x in customers | orderBy : '-city'">{{x.name + ", " + x.city}}</li>

Sort the array by "city":

<li ng-repeat="x in customers | orderBy : 'city'">{{x.name + ", " + x.city}}</li>

Upvotes: -1

Bo Vandersteene
Bo Vandersteene

Reputation: 639

Angular still advice not to use pipes for sorting and filtering, like in angularJs.

It will slow down your application, have some performance issues. It is a lot better to provide your sorting in your component before passing it to the template.

The reason is explained on https://angular.io/guide/pipes#no-filter-pipe

If you work with a nice structured component layout you can do it even on te setter:

 @Input()
  set users(users: Array<User>) {
    this.usersResult = (users || []).sort((a: User, b: User) => a.name < b.name ? -1 : 1)
  }

Upvotes: 10

Meir
Meir

Reputation: 14395

Your pipe expects strings but it gets objects, you should adapt it:

export class ArraySortPipe implements PipeTransform {
  transform(array: Array<any>): Array<string> {
    array.sort((a: any, b: any) => {
      if (a.name < b.name) {
        return -1;
      } else if (a.name > b.name) {
        return 1;
      } else {
        return 0;
      }
    });
    return array;
  }
}

Upvotes: 13

petetetete
petetetete

Reputation: 465

Although you can solve this problem with a pipe, you have to ask yourself if the re-usability of a pipe is useful to you in your particular project. Will you often need to sort objects by the "name" key on other arrays or other components in the future? Will this data be changing often enough and in ways that make it hard to simply sort in the component? Will you need the array sorted on any change to the view or inputs?

I created an edited plunker in which the array is sorted in the component's constructor, but there's no reason this functionality couldn't be moved out into its own method (sortValuesArray for instance) for re-use if necessary.

constructor() {
  this.values.sort((a, b) => {
    if (a.name < b.name) return -1;
    else if (a.name > b.name) return 1;
    else return 0;
  });
}

Edited Plunker

Upvotes: 25

Related Questions