title: Angular 5: How do I sort Observable alphabetically?
body: <p>I have a problem I can't figure out: I want to sort data in an Observable (I assume it is at least) alphabetically.</p>
  
  <p>What the code basically does is get the data from Firestore and display the startHour and startMinute variables it gets from the database inside an event title bar. That works fine. However, it does not sort the 'titles' properly. Rather than sort them alphabetically (as in, earlier times first i.e. 07:20 in front of 10:50, then 13:05) it sorts it based on the document ids shown in the database. So basically it can become 07:20, then 13:05, then 10:50, which is not what I want.</p>
  
  <p>calendar.component.ts:</p>
  
  <pre><code>import { Component, OnInit, ViewEncapsulation } from '@angular/core';
  import { FormGroup } from '@angular/forms';
  import { Subject } from 'rxjs/Subject';
  import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
  import { Observable } from 'rxjs/Observable';
  
  import { MatDialog, MatDialogRef } from '@angular/material';
  import { startOfDay, isSameDay, isSameMonth } from 'date-fns';
  
  import { CalendarEvent, CalendarEventAction, CalendarEventTimesChangedEvent, CalendarMonthViewDay } from 'angular-calendar';
  
  import { FuseConfirmDialogComponent } from '@fuse/components/confirm-dialog/confirm-dialog.component';
  import { fuseAnimations } from '@fuse/animations';
  
  import { FuseCalendarEventFormDialogComponent } from './event-form/event-form.component';
  import { CalendarEventModel } from './event.model';
  
  import 'rxjs/add/operator/map';
  
  export interface Ride { allDay: boolean; color: Object; /*primary: string; secondary: string;*/ end: Date; meta: Object; location: string; notes: string; start: Date; title: string; draggable: boolean; }
  export interface RideId extends Ride { id: string }
  
  @Component({
      selector     : 'fuse-calendar',
      templateUrl  : './calendar.component.html',
      styleUrls    : ['./calendar.component.scss'],
      encapsulation: ViewEncapsulation.None,
      animations   : fuseAnimations
  })
  export class FuseCalendarComponent implements OnInit
  {   
      view: string;
      viewDate: Date;
      events: CalendarEvent[];
      locale: string = 'nl';
      public actions: CalendarEventAction[];
      activeDayIsOpen: boolean;
      refresh: Subject&lt;any&gt; = new Subject();
      dialogRef: any;
      confirmDialogRef: MatDialogRef&lt;FuseConfirmDialogComponent&gt;;
      selectedDay: any;
      rides: Observable&lt;RideId[]&gt;;
      ride: Observable&lt;Ride&gt;;
  
      private ridesCollection: AngularFirestoreCollection&lt;Ride&gt;;
      private ridesDocument: AngularFirestoreDocument&lt;Ride&gt;;
  
  
  
      vehicles = [
          {value: 'vehicle-0', viewValue: 'Alle'},
          {value: 'vehicle-1', viewValue: 'Buurtmobiel Beuningen/Weurt'},
          {value: 'vehicle-2', viewValue: 'Buurtmobiel Ewijk/Winssen'}
      ];
  
      constructor(
          public dialog: MatDialog,
          private readonly db: AngularFirestore,
      )
      {
          this.view = 'month';
          this.viewDate = new Date();
          this.activeDayIsOpen = true;
          this.selectedDay = {date: startOfDay(new Date())};
  
  
  
          this.actions = [
              {
                  label  : '&lt;i class="material-icons s-16"&gt;edit&lt;/i&gt;',
                  onClick: ({event}: { event: CalendarEvent }): void =&gt; {
                      this.editEvent('edit', event);
                  }
              },
              {
                  label  : '&lt;i class="material-icons s-16"&gt;delete&lt;/i&gt;',
                  onClick: ({event}: { event: CalendarEvent }): void =&gt; {
                      this.deleteEvent(event);
                  }
              }
          ];
  
          /**
           * Get events from service/server
           */
           this.ridesCollection = db.collection&lt;Ride&gt;('rides');
  
           this.rides = this.ridesCollection.snapshotChanges().map(actions =&gt; {
              return actions.map(a =&gt; {
              let data = a.payload.doc.data() as Ride;
                  data.draggable = true;
              const id = a.payload.doc.id;
              return { id, ...data };
        });       
      });
  
  //        this.setEvents();
      }
  
      ngOnInit()
      {
          /**
           * Watch re-render-refresh for updating db
           */
  //        this.refresh.subscribe(updateDB =&gt; {
  //            // console.warn('REFRESH');
  //            if ( updateDB )
  //            {
  //                // console.warn('UPDATE DB');
  //                this.calendarService.updateEvents(this.events);
  //            }
  //        });
  
  //        this.calendarService.onEventsUpdated.subscribe(events =&gt; {
  //            this.setEvents();
  //            this.refresh.next();
  //        });
  
  //        this.ridesDocument = this.db.doc('rides/id');
  //        this.ride = this.ridesDocument.valueChanges();
      }
  
      /**
       * Before View Renderer
       * @param {any} header
       * @param {any} body
       */
      beforeMonthViewRender({header, body})
      {
          // console.info('beforeMonthViewRender');
          /**
           * Get the selected day
           */
          const _selectedDay = body.find((_day) =&gt; {
              return _day.date.getTime() === this.selectedDay.date.getTime();
          });
  
          if ( _selectedDay )
          {
              /**
               * Set selectedday style
               * @type {string}
               */
              _selectedDay.cssClass = 'mat-elevation-z3';
          }
  
      }
  
      /**
       * Day clicked
       * @param {MonthViewDay} day
       */
      dayClicked(day: CalendarMonthViewDay): void
      {
          const date: Date = day.date;
          const events: CalendarEvent[] = day.events;
  
          if ( isSameMonth(date, this.viewDate) )
          {
              if ( (isSameDay(this.viewDate, date) &amp;&amp; this.activeDayIsOpen === true) || events.length === 0 )
              {
                  this.activeDayIsOpen = false;
              }
              else
              {
                  //Titlebar(s) that appear(s) when a day has been selected
  
                  this.activeDayIsOpen = true;
                  this.viewDate = date;
              }
          }
          this.selectedDay = day;
          this.refresh.next();
      }
  
      /**
       * Event times changed
       * Event dropped or resized
       * @param {CalendarEvent} event
       * @param {Date} newStart
       * @param {Date} newEnd
       */
      eventTimesChanged({event, newStart, newEnd}: CalendarEventTimesChangedEvent): void
      {
          event.start = newStart;
          event.end = newEnd;
  
  //        console.warn('Dropped or resized', event);
  
          this.ridesDocument = this.db.doc('rides/' + event.id);
          console.log(event.start);
          console.log(newStart);
          this.ridesDocument.update(event);
          this.refresh.next(true);
      }
  
      /**
       * Delete Event
       * @param event
       */
      deleteEvent(ride)
      {
          this.confirmDialogRef = this.dialog.open(FuseConfirmDialogComponent, {
              disableClose: false
          });
  
          this.confirmDialogRef.componentInstance.confirmMessage = 'Are you sure you want to delete?';
  
          this.confirmDialogRef.afterClosed().subscribe(result =&gt; {
              if ( result )
              {
  //                const eventIndex = this.events.indexOf(event);
  //                this.events.splice(eventIndex, 1);
  //                this.refresh.next(true);
  
              }
              this.confirmDialogRef = null;
          });
  
      }
  
      /**
       * Edit Event
       * @param {string} action
       * @param {CalendarEvent} event
       */
      editEvent(action: string, event: CalendarEvent)
      {
  //        const eventIndex = this.events.indexOf(event);
  
          this.dialogRef = this.dialog.open(FuseCalendarEventFormDialogComponent, {
              panelClass: 'event-form-dialog',
              data      : {
                  event : event,
                  action: action
              }
          });
  
          this.dialogRef.afterClosed()
              .subscribe(response =&gt; {
                  if ( !response )
                  {
                      return;
                  }
                  const actionType: string = response[0];
                  const formData: FormGroup = response[1];
                  switch ( actionType )
                  {
                      /**
                       * Save
                       */
                      case 'save':
                          let saveData: any;
  //                        this.events[eventIndex] = Object.assign(this.events[eventIndex], formData.getRawValue());
                          this.refresh.next(true);
  
                          saveData = formData.getRawValue();
  //                        saveData.start.setHours(saveData.startHour);
  //                        saveData.start.setMinutes(saveData.startMinute);
  
                          this.ridesDocument = this.db.doc('rides/' + event.id);
                          saveData.start.setHours(saveData.startHour, saveData.startMinute);
                          saveData.end.setHours(saveData.endHour, saveData.endMinute);
                          this.ridesDocument.update(saveData);
  
                          break;
                      /**
                       * Delete
                       */
                      case 'delete':
  
  //                        this.deleteEvent(event);
                          this.ridesDocument = this.db.doc('rides/' + event.id);
                          this.ridesDocument.delete();
  
                          break;
                  }
              });
      }
  
      /**
       * Add Event
       */
      addEvent(): void
      {
          this.dialogRef = this.dialog.open(FuseCalendarEventFormDialogComponent, {
              panelClass: 'event-form-dialog',
              data      : {
                  action: 'new',
                  date  : this.selectedDay.date
              }
          });
          this.dialogRef.afterClosed()
              .subscribe((response: FormGroup) =&gt; {
                  if ( !response )
                  {
                      return;
                  }
                  const newEvent = response.getRawValue();
  //                newEvent.actions = this.actions;
  //                this.events.push(newEvent);
                  this.refresh.next(true);
                  newEvent.start.setHours(newEvent.startHour, newEvent.startMinute);
                  newEvent.end.setHours(newEvent.endHour, newEvent.endMinute);
                  this.ridesCollection.add(newEvent);
              });
      }
  }
  </code></pre>
  
  <p>I've tried using a pipe, as well as .sort() and orderBy, but it likes nothing. Perhaps I've done it in a wrong way, I cannot say, so I would love some help with this problem.</p>
  
  <p>I use Fuse2 with Angular 5. The database is Angularfire2 Firestore.</p>
  
  <p>Any help will be appreciated!</p>
  
  <p>EDIT: I am not sure if this helps in any way, but I am able to put .sort() in here:</p>
  
  <pre><code>this.rides = this.ridesCollection.snapshotChanges().map(actions =&gt; {
              return actions.map(a =&gt; {
              let data = a.payload.doc.data() as Ride;
                  data.draggable = true;
              const id = a.payload.doc.id;
              return { id, ...data };
        }).sort(); // Can add .sort() here without an error, but nothing happens
      });
  </code></pre>
  
  <p>But as comment says, it does nothing and whenever I try to put an argument between the () I get an error message...</p>