Tips for sorting an array of objects by multiple keys while maintaining the order of each key that comes before

I am looking to create a versatile function that can organize an array of objects based on specified keys, while maintaining the order of previous keys. Here is a sample scenario:

const input = [
  { a: 'aardvark', b: 'bear', c: 'camel', d: 1 },
  { a: 'anemone', b: 'bat', c: 'cobra', d: 6 },
  { a: 'aardvark', b: 'badger', c: 'camel', d: 2 },
  { a: 'alligator', b: 'bat', c: 'chicken', d: 2 },
  { a: 'alligator', b: 'beetle', c: 'cow', d: 1 },
  { a: 'alligator', b: 'bat', c: 'crab', d: 3 },
]

sortFunction(['a', 'b', 'd'], input)

// output
[
  { a: 'aardvark', b: 'badger', c: 'camel', d: 2 },
  { a: 'aardvark', b: 'bear', c: 'camel', d: 1 },
  { a: 'alligator', b: 'bat', c: 'chicken', d: 2 },
  { a: 'alligator', b: 'bat', c: 'crab', d: 3 },
  { a: 'alligator', b: 'beetle', c: 'cow', d: 1 },
  { a: 'anemone', b: 'bat', c: 'cobra', d: 6 },
]

The initial sorting key is a, so the output correctly arranges the data by a in descending order. The subsequent key in the list is b, which then reorders items with matching values of a based on the b keys. Finally, d is used to reorder items with identical values of a and b. For example, if there are two items with a: alligator and b: bat, the item with d: 2 is positioned before the one with d: 3.

The challenge lies in transforming this specific example into a generic function capable of accepting any array of objects and organizing them according to a defined list of keys, where the values of those keys are either strings or numbers.

Answer №1

To sort your input data, one effective method is to utilize the `sort()` function along with a comparator callback that iterates through each key until it establishes the correct sorting order:

function customSort<K extends PropertyKey, T extends Record<K, string | number>>(
  keys: K[], objects: T[]
) {
  objects.sort((a, b) => {
    for (const key of keys) {
      if (a[key] < b[key]) return -1;
      if (a[key] > b[key]) return 1;
    }
    return 0;
  })
}

This function is designed as a generic one, where the `objects` parameter enforces that the array of objects contains values of type `string` or `number` at the specified keys in the `keys` array.

You can test this functionality on your sample input using the code snippet below:

customSort(['a', 'b', 'd'], inputData);

console.log(inputData.map(value => JSON.stringify(value)).join("\n"));    
/*
"{"a":"aardvark","b":"badger","c":"camel","d":2}
{"a":"aardvark","b":"bear","c":"camel","d":1}
{"a":"alligator","b":"bat","c":"chicken","d":2}
{"a":"alligator","b":"bat","c":"crab","d":3}
{"a":"alligator","b":"beetle","c":"cow","d":1}
{"a":"anemone","b":"bat","c":"cobra","d":6}" ]
*/

You can also experiment with the code using this Playground link provided.

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

Angular6 HttpClient: Unable to Inject Headers in Get Request for Chrome and IE11

Under my Angular 6 application, I am attempting to make a GET request while injecting some custom Headers: Here is how my service is structured: @Injectable() export class MyService { constructor(public httpClient: HttpClient) { } getUserInfos(login): Ob ...

NextJS and Context API throwing a Typescript error

I've been working on my _app.tsx file and here's the code snippet I have: import React from 'react' import type { AppProps } from 'next/app' /* Import Styles */ import '@themes/index.scss' /* Import Template */ imp ...

The recommended filename in Playwright within a Docker environment is incorrectly configured and automatically defaults to "download."

Trying to use Playwright to download a file and set the filename using download.suggestedFilename(). Code snippet: const downloadPromise = page.waitForEvent('download', {timeout:100000}) await page.keyboard.down('Shift') await p ...

A guide on managing Ngb Bootstrap carousel slide with a button in Angular

I encountered a situation like this: I need to implement a Ngb Bootstrap carousel with buttons for Previous and Next to control the slide images. Clicking on the Previous button should display the previous slide image, and clicking on the Next button shou ...

Encountering Error with NodeJS Typescript: Issue with loading ES Module when running sls offline command

I have come up with a unique solution using NodeJS, Typescript, and Serverless framework to build AWS Lambdas. To debug it locally in VS Code, I use the serverless-offline library/plugin. You can find my project on GitHub here However, when I run the comm ...

What could be the reason for my dynamic image not appearing in a child component when using server-side rendering in Nuxt and Quasar

Currently, I am tackling SSR projects using Nuxt and Quasar. However, I encountered an issue when trying to display a dynamic image in a child component as the image is not being shown. The snippet of my code in question is as follows: function getUrl (im ...

Utilizing JavaScript variables imported from an external library in Next.js: A Guide

I am currently working on a Next.js with Typescript website and I am in the process of adding advertisements. The ad provider has given me instructions to embed this JavaScript code on my site: <script src="//m.servedby-buysellads.com/monetization. ...

encountered an issue when testing a dynamic route in Next.js with postman

I recently created a new API route named route.ts, where I included two different routes. One route retrieves all users from the database, while the other retrieves a specific user based on their ID passed as a query parameter. However, when testing these ...

The magical form component in React using TypeScript with the powerful react-final-form

My goal is to develop a 3-step form using react-final-form with TypeScript in React.js. I found inspiration from codesandbox, but I am encountering an issue with the const static Page. I am struggling to convert it to TypeScript and honestly, I don't ...

The TypeScript Promise error codes TS2304 and TS2529 are causing confusion among

I came across the code below: function asyncTask(): Promise<string> { return new Promise<string>(resolve => resolve); } This code resulted in the following error: TS2304: cannot find name 'Promise' To address this issue, ...

What could be causing the lack of Tailwind CSS intellisense in a Next.js app with TypeScript?

Check out my tailwind.config.ts file I've been trying to figure it out by watching YouTube tutorials, but nothing seems to be working. Even with the tailwind.config.ts file in my Next.js app, it still isn't functioning properly. Perhaps there&ap ...

Unexpected Secondary Map Selector Appears When Leaflet Overlay is Added

Working on enhancing an existing leaflet map by adding overlays has presented some challenges. Initially, adding different map types resulted in the leaflet selector appearing at the top right corner. However, when attempting to add LayerGroups as overlays ...

The specified type '{ state: any; dispatch: React.Dispatch<{ type: string; value: any; }>; }' is not compatible with the expected type

I've been working on a UI layout that includes checkboxes on the left, a data table on the right, and a drop zone box. The aim is to keep the table data updated whenever a new file is dropped, and also filter the data based on checkbox selection. I ma ...

Maintain the spacing of an element when utilizing *ngFor

Using Angular.js and *ngFor to loop over an array and display the values. The goal is to preserve the spaces of elements in the array: string arr1 = [" Welcome Angular ", "Line1", "Line2", " Done "] The ...

The value returned by elementRef.current?.clientHeight is not the correct height of the element

I've encountered a peculiar issue with my code where the reported height of an element does not match its actual size. The element is supposed to be 1465px tall, but it's showing up as 870px. I suspect that this discrepancy might be due to paddin ...

The process of removing and appending a child element using WebDriverIO

I am trying to use browser.execute in WebDriverIO to remove a child element from a parent element and then append it back later. However, I keep receiving the error message "stale element reference: stale element not found". It is puzzling because keepin ...

Tips for activating automatic building of packages when utilizing pnpm install?

Our unique project is utilizing a combination of pnpm, workspace, and typescript in adherence to the monorepo standard. Upon cloning the repository, we execute a pnpm install command to download dependencies and establish links between local packages. De ...

Enhancing TypeScript with Generic Proxyify Functionality

I'm attempting to enclose a basic interface provided through a type generic in order to alter the return value of each function within the interface. For instance: interface IBaseInterface { test(a?: boolean, b?: number): Promise<boolean>; ...

Guide on incorporating secondary endpoints to develop an API for an Angular library

Challenge: I recently developed a customized UI library using ng-packagr, where I exported unique components along with some model classes. Issue: While the import statement functions correctly for the exported components in my main project, it fails to ...

Issue with Angular UI Bootstrap accordion heading behavior when used in conjunction with a checkbox is causing

I have implemented a checkbox in the header of an accordion control using Bootstrap. However, I am facing an issue where the model only updates the first time the checkbox is clicked. Below is the HTML code for the accordion: <accordion ng-repeat="tim ...