Home > Back-end >  Sort users in nested array alphabetically[Angular]
Sort users in nested array alphabetically[Angular]

Time:07-24

I have a nested array and I'm using ngFor to iterate the array.

For example: I have a list of cities, under the cities I display users.

I am trying to sort all users inside the city alphabetically.

Sample code on stackblitz

HTML

<div *ngFor="let data of locations$ | async">
{{ data.name }}

<ul *ngFor="let permission of getPermissions(data) | async">
 <ng-container *ngFor="let user of permission.users">
  <li>{{ user.lastName }}, {{ user.firstName }}</li>
 </ng-container>
</ul>
</div>

Component:

results = of(this.res);

locations$: Observable<any>;
permissions$: Observable<any>;
constructor() {
this.locations$ = this.results.pipe(map((x) => x.locations));
this.permissions$ = this.results.pipe(
  map((x) => x.permissions),
  tap((data) => {
    data.sort((a, b) => (a.lastName > b.lastName ? 1 : -1));
  })
);
}

getPermissions(data) {
return this.permissions$.pipe(
  map((permission) =>
    permission.filter((x) => {
      return x.entity.uuid === data.uuid;
    })
  )
);
}
}

Note: Need access to the other properties inside permissions array (eg: entity & name) to perform other logic in component. Therefore, cannot create a separate array for users

CodePudding user response:

If it's nested then you should sort nested array.

Instead of

tap((data) => {
  data.sort((a, b) => (a.lastName > b.lastName ? 1 : -1));
})

try looping over permissins first and inside each persmission you can sort users:

tap((permissions) => {
  permissions.forEach(permission => 
       permission.users.sort((a, b) => a.lastName.localeCompare(b.lastName))
  )
})

Note: I also used String.prototype.localeCompare() in order to not to think about case-insensitive strings

Forked Stackblitz

Update

Since you want to filter users within each city then I would suggest you restructure your locations$ observable like:

locations$ = this.results.pipe(map(res => {
  return res.locations.map(location => {
    const permissions =  res.permissions.filter((x) => x.entity.uuid === location.uuid);
    const users = permissions.map(p => p.users.map(user => ({...user, permission: p}))).reduce((acc, item) => acc.concat(item), []).sort((a, b) =>a.lastName.localeCompare(b.lastName))
    return {
      ...location,
      users
    }
  })
}));

Now you have everything in one place and you don't need to restructure a new Observable by calling getPermissions(data) on each Angular change detection cycle.

<div *ngFor="let location of locations$ | async">
  {{ location.name }}
  <ul>
      <li *ngFor="let user of location.users">>{{ user.lastName }}, {{ user.firstName }} <b>({{ user.permission.name}})</b></li>
  </ul>
</div>

Stackblitz Update

  • Related