Is there a way to sort the output of an observable in various different methods?

One interesting feature I have implemented is a TableData type observable that provides me with a collection of table rows and columns. The user has the option to select a set of columns from a dropdown menu (which corresponds to the rows) to be sorted in ascending order by default.

This can be demonstrated by using an example where the user inputs "employee" and "project". In this case, the lines are first sorted by employee, and then each individual project is sorted in ascending order for that particular employee.

The relevant class types are as follows:


export interface TableColumn {
  value: string;
  label: string;
  hidden: boolean;
  format: (value: any) => string;
}

export interface TableRow {
  [key: string]: any;
}

export interface TableData {
  columns: TableColumn[];
  rows: TableRow[];
}
export class Activity {
  id?: number;
  [HOURS_KEY]: number;
  [DATE_KEY]: string;
  [EMPLOYEE_KEY]: ActivityEmployee;
  [TYPE_KEY]: ActivityType;
  [PROJECT_KEY]?: ActivityProject;
}

export class ActivityEmployee {
  id: number;
  lastName: string;
  firstName: string;
}

If I were to sort by Employee and Project only (as a static example), how would I go about ordering the observables I receive in a chained manner?

I currently call the observable results in the following way:


tableDataColumns$ = this.activityCollection.groupedEntities$?.pipe(
    map((e) => e.columns)
  );
  tableDataRows$ = this.activityCollection.groupedEntities$?.pipe(
    map((e) => e.rows)
  );

Answer №1

sortCriteria = new Subject<[string, string]>();

sortedTableData$ = this.sortCriteria.pipe(
  startWith(["", ""]),
  switchMap(([sortLabel1, sortLabel2]) =>
    this.activityCollection.groupedEntities$.pipe(
      map(({ rows }) => rows),
      map(rows =>
        !!sortLabel1 ? rows.sort((a, b) => (a[sortLabel1] < b[sortLabel1] ? -1 : 1)) : rows
      ),
      map(rows => (!!sortLabel2 ? this.splitAndSortRows(rows, sortLabel1, sortLabel2) : rows))
    )
  )
);

splitAndSortRows = <T>(rows: T[], splitBy: string, sortBy: string): T[] => {
  const uniqueValues = [...new Set(rows.map(row => row[splitBy]))];
  const splitArray = uniqueValues.map(value =>
    rows.filter(row => row[splitBy] === value)
  );
  return splitArray.reduce(
    (acc, arrSegment) => [
      ...acc,
      ...arrSegment.sort((a, b) => a[sortBy] < b[sortBy] ? -1 : 1),
    ],
    [] as T[]
  );
};

We initiate a subject to emit a tuple representing the two labels for sorting. We then establish our pipe with the following sequence:

  1. Subscribe to the subject to receive the latest sorting criteria
  2. Switch to the source data observable.
  3. Use map() with object destructuring to obtain our rows data.
  4. Sort the rows array based on the first criteria using .sort().
  5. Invoke a function to further sort the data based on the second label.

The functionality of splitAndSortRows() is explained below:

  1. Generate an array of distinct values for the first sorting criterion.
  2. Separate the main array into multiple arrays based on each unique value.
  3. Sort each sub-array and combine them back into one array.

Now, when a user selects one or two labels for sorting, these labels can be emitted through the sortCriteria subject. The subscription will handle the sorting process.

this.sortCriteria.next(['employee', 'project']);
// sortedTableData$ will now emit a new array of rows sorted by employee first, and projects second

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for Mastering Animation Anchoring with Angular 2

Did you know that AngularJS 1.4 has a unique animation feature called Animation Anchoring? This feature allows you to mark elements in both the source and destination pages with the attribute ng-animate-ref, creating a computed animation between the two b ...

What is the best way to transfer my static files to the desired output directory in a TypeScript Express application?

I am attempting to transfer my static files from the input directory to the output directory using Express. I found guidance in this tutorial, which utilized shell.js for copying static files. The code responsible for this operation is located in CopyAsse ...

Protractor syncing with an Angular page following redirection to an Auth0 non-Angular page

My Angular web application utilizes Protractor for end-to-end tests. Recently, I implemented OAuth0 authentication into my app. To disable Angular synchronization before redirecting to the non-Angular OAuth0 page, I use await browser.waitForAngularEnabled( ...

How to immediately set focus on a form control in Angular Material without needing a click event

Currently working with Angular 9 and Material, we have implemented a Stepper to assist users in navigating through our workflow steps. Our goal is to enable users to go through these steps using only the keyboard, without relying on mouse clicks for contro ...

Updating the log file location for electron-log in an Angular application integrated with Electron

I am currently developing a project using Angular 6 integrated with Electron. I have managed to successfully incorporate the electron-log library using ngx-electron. As a result, my application is functioning well and logging data to the default path: C:&b ...

Initial attempt with Angular2 router.navigate() fails to function properly

I have set up the routes as follows: export const routes: Routes = [ { path: '', component: HomeComponent, pathMatch: 'full', canActivate: [AuthGuardService] }, { path: 'sites', component: SiteIndexComponent, resolve: ...

Angular - ngbDropdownMenu items are hidden from view, but their presence is still detectable

Hey there! I'm currently working on a school project and I'm aiming to implement a drop-down menu. Surprisingly, it's proving to be more challenging than expected. <div class="parrent"> <div class="row"> ...

issue encountered when implementing slick.js in angular 6

Trying to implement slick.js, a carousel framework, in an Angular project. Initially attempted 'npm install slick --save' and manually adding the downloaded files to scripts and styles JSON objects. When that failed, resorted to importing the C ...

Out of nowhere, encountering TS2322 Typescript errors, causing type mismatches during the compilation phase

I am facing an issue with AWS Codebuild while deploying APIs written in lambda and exposed via API Gateway using Koa. The build process is throwing an error related to type assignment. src/components/chart-color-settings/chart-color-settings.ts(11,13): err ...

How to Use TypeScript to Disable Href in Angular

I've encountered a challenge with disabling an href link using Angular and Typescript, and I'm unsure if my current approach is the right one. Is there a more optimal way to achieve something like this? I would like it to resemble the red circle ...

What is the best way to explain a function that alters an object directly through reference?

I have a function that changes an object's properties directly: function addProperty(object, newValue) { object.bar = newValue; } Is there a way in TypeScript to indicate that the type of object is modified after calling the addProperty function? ...

I am experiencing issues with my Jest unit test case for Material UI's multi select component failing

I've been working on writing a unit test case for the material UI multi select component. The code for the parent component is as follows: import {myData} from '../constant'; export const Parent = () => { const onChangeStatus= (sel ...

Facing issues with module resolution while attempting to debug in VSCode

I'm currently in the process of debugging a module within our project. However, I've encountered difficulties attaching the debugger on Visual Studio Code since we recently changed the directory structure. Here is my tsconfig: { "compilerOptio ...

Mastering the art of filtering arrays in RxJS 6 using Observables and async functions

I am currently working with an Observable that returns data and is being bound asynchronously in HTML. Here is where I make the service call: this.ProductOptions = this.ProductService.fetchProduct(); The HTML binding looks like this: Productoptions | a ...

Utilizing the index of the .map function in conjunction with internal methods

After running my code, I encountered the following error message: Warning: Encountered two children with the same key, `classroom-1278238`. Keys are required to be unique so that components can maintain their identity during updates. Having non-unique keys ...

Is it possible for the binding model of Mat-Checkbox to be optional or null?

Greetings everyone! I am a newcomer to the world of Angular, where I have successfully created a dynamic list of checkboxes. However, I have encountered a challenge when trying to bind selected values from an API to these checkboxes. <div *ngFor="let b ...

What is the reason behind Rxjs switchMap only emitting the final value from an of() observable source?

Here are two code snippets, one using map and the other using switchMap. The functionality of map is clear: of('foo', 'bar') .pipe(map((val) => sanitizer(val))) .subscribe((val) => console.log('value:', val)); func ...

Is the return type of 'void' being overlooked in TypeScript - a solution to avoid unresolved promises?

When working in TypeScript 3.9.7, the compiler is not concerned with the following code: const someFn: () => void = () => 123; After stumbling upon this answer, it became apparent that this behavior is intentional. The rationale behind it makes sens ...

Configuration file for proxying both HTTP requests and WebSockets to the same endpoint

In my development environment, I have successfully set up a proxy for http requests to a django server on my backend/laptop using a proxy.conf.json configuration file: { "/graphql": { "target": "https://localhost:443/gr ...

Tips for retrieving the most recent UI updates after the container has been modified without the need to refresh the browser

Currently, I have developed a micro frontend application in Angular using module federation. This application is hosted in production with Docker containers. My main concern revolves around how to update the UI changes for the user without them needing to ...