HOW TO . . .

How to sort numbers and strings?

I create a sort function that places numbers and strings in the expected order.

ProgrammingCouple
6 min readJan 19, 2024
Presentation of the operation of a complex function that sorts numbers and strings
The complex function that can sort numbers and strings

This code will work for both numbers and strings, but also for numbers and strings separately.

Presentation of the operation of a complex function that sorts numbers
The complex function that can sort numbers
Presentation of the operation of a complex function that sorts strings
The complex function that can sort strings

As usual, we start with the application setup. I prepared examples using NodeJS with TypeScript. If you don’t have nodemon and ts-node packages already installed, open a terminal and enter the following commands:

npm i -g nodemon
npm i -g ts-node

I installed both packages globally by adding the -g flag to the command. If you are on Mac, and after that, you receive “Error: EACCES: permission denied”, it means you should repeat those steps but add sudo before both commands.

Create our new project folder in the location of your choice. I named my folder sort-num-and-str. Then inside this folder, I create a .ts file with the same name. In this file, we will implement our sorting function.

Next, create a file named DEFAULT_DATA.ts in the ./src directory. The file will store the sample data we use in these examples. You can create your data file. The most important thing for the consistency of the following solutions is to keep the structure as an array of numbers and/or strings. It would be great if you combined different numbers and strings in this array to see our sorting results better, in the style of my data file below:

// File name: DEFAULT_DATA.ts

export const DEFAULT_DATA = [
0,
"Dog",
"Cat",
"Hamster",
1,
-37,
100,
"Lion",
"Cow",
13,
"Pig",
"Sheep",
"Zebra",
55,
0.77,
"Moose",
24,
"Tiger",
"Shark",
"Shrimp",
];

Now we want to handle basic sorting, which means we need to follow some self-determined default assumptions:

  • numbers go first
  • strings go next
  • both numbers and strings are sorted in ascending order

It’s worth to have in mind what kind of combinations we will face to handle. Our sort function is based on two values which are next to each other. We allow two data types: numbers and strings.

2 values * 2 data types = 4 possible combinations

Let’s list them out:

  1. string & string
  2. string & number
  3. number & string
  4. number & number
// File name: sortNumAndStr.ts

type SortValueType = number | string;

export const sortNumAndStr = (a: SortValueType, b: SortValueType) => {
const aStr = a.toString();
const bStr = b.toString();

const aNum = Number(a);
const bNum = Number(b);

const aIsStr = isNaN(aNum);
const bIsStr = isNaN(bNum);

let order: number;

if (aIsStr === bIsStr) {
order = aIsStr ? aStr.localeCompare(bStr) : aNum - bNum;
} else {
order = aIsStr ? 1 : -1;
}

return order;
};

Let’s talk about what happened.

First, we prepare some useful variables for better readability of subsequent lines of code with conditions. The next step is to formulate the condition itself.

In the first block of the if-else statement, we check if values have the same data type because this fulfilled condition will give us TRUE only when aIsStr and bIsStr are both true or both false. In this one condition, we are dealing with points 1 and 4 from the list of possible scenarios. That’s why, we assign to the order variable the appropriate comparison type according to any sort of value (because, as I said, we know that we have values ​​of the same data type when we pass this condition). In this example, I’m using a ternary operator with the aIsStr variable.

In the second block of the if-else statement, we handle the rest of the scenarios (points 2 and 3). If the first value is a string, it means the second value must be a number. In this situation, we want to replace the values. Otherwise, we leave these values ​​unchanged.

As you can see, we return 1 or -1. Also, our first condition returns a positive value or a negative value. Those values are information returned to the sort function and based on this information values ​​are moved up or down. Here is the short recap:

What values we should return in the ascending sort function?

  • 1 or positive values => move value a DOWN (under value b)
  • 0 => nothing has changed, but the sort function stops further sorting value a
  • -1 or negative values => move value a UP (above value b)***
  • ** WAIT! This doesn’t make sense because the value of a is already above the value of b. Yes, but this is feedback to the sort function to continue sorting — as opposed to when it was 0. We can say — nothing has changed, but the sorting function continues.

What values we should return in the descending sort function?

  • 1 or positive values => move value a UP (above value b)
  • 0 => nothing has changed, but the sort function stops further sorting value a
  • -1 or negative values => move value a DOWN (under value b)

Check the results of your newly created sort function by adding some console logs:

// File name: sortNumAndStr.ts

/*

<----- sortNumAndStr function code goes here ----->

*/

const sortedData = [...DEFAULT_DATA].sort((a, b) => sortNumAndStr(a, b));

console.log("original data", DEFAULT_DATA);
console.log("sorted data", sortedData);
Basic sort function results are displayed in the terminal
Basic sort function results are displayed in the terminal

Now let’s make something more complex and reusable. I don’t know about you, but I would like to have a sorting function that changes its behavior depending on the additional options I provide. I would like to influence the type of number order, the type of string order, and whether numbers or strings come first.

// File name: sortNumAndStr.ts

type SortValueType = number | string;
type SortOptionsType = {
descNum: boolean;
descStr: boolean;
strFirst: boolean;
};

const defaultOptions = { descNum: false, descStr: false, strFirst: false };

export const sortNumAndStr = (
a: SortValueType,
b: SortValueType,
options: SortOptionsType = defaultOptions
) => {
const { descNum, descStr, strFirst } = options;

const aStr = a.toString();
const bStr = b.toString();

const aNum = Number(a);
const bNum = Number(b);

const aIsStr = isNaN(aNum);
const bIsStr = isNaN(bNum);

let order: number;

if (aIsStr === bIsStr) {
const numSubtraction = descNum ? bNum - aNum : aNum - bNum;
const strComparison = descStr
? bStr.localeCompare(aStr)
: aStr.localeCompare(bStr);

order = aIsStr ? strComparison : numSubtraction;
} else {
const primitiveTypesOrder = strFirst ? -1 : 1;
order = aIsStr ? primitiveTypesOrder : -primitiveTypesOrder;
}

return order;
};

Let’s test it out!

// File name: sortNumAndStr.ts

/*

<----- sortNumAndStr function code goes here ----->

*/

const sortedDataDefaultOptions = [...DEFAULT_DATA].sort((a, b) =>
sortNumAndStr(a, b)
);
const sortedDataSelectedOptions = [...DEFAULT_DATA].sort((a, b) =>
sortNumAndStr(a, b, { descNum: true, descStr: false, strFirst: true })
);

console.log("original data", DEFAULT_DATA);
console.log("sorted data - default options", sortedDataDefaultOptions);
console.log(
"sorted data - options: { descNum: true, descStr: false, strFirst: true }",
sortedDataSelectedOptions
);
Presentation of the operation of a complex function that sorts numbers and strings
Presentation of the operation of a complex sorting function

HINT: Remember that you can always play with the order of passed values. Look at the code below, I am replacing the values ​​of a and b.

// File name: sortNumAndStr.ts

/*

<----- sortNumAndStr function code goes here ----->

*/

const sortedDataDefault = [...DEFAULT_DATA].sort((a, b) => sortNumAndStr(a, b));
const sortedDataReplaced = [...DEFAULT_DATA].sort((a, b) => sortNumAndStr(b, a));

console.log("(a, b) => sortNumAndStr(a, b)", sortedDataDefault);
console.log("(a, b) => sortNumAndStr(b, a)", sortedDataReplaced);
ort function results are displayed in two variants of order: (a, b) and (b, a)
Sort function results are displayed in two variants of order: (a, b) and (b, a)

Notice that replacing a and b like so…

(a, b) => sortNumAndStr(b, a)

…is equal to the following notation:

(a, b) => sortNumAndStr(a, b, { descNum: true, descStr: true, strFirst: true })
Comparison of the sort function results displayed in two notation variants
Comparison of the sort function results displayed in two notation variants

I invite you also to read my other articles.

Thank you for reading, let’s get applause 👏 and see you next time!

--

--

ProgrammingCouple
ProgrammingCouple

Written by ProgrammingCouple

See programmingcouple.com with useful tools and statistics for Medium Members

No responses yet