mirror of
https://github.com/toss/es-toolkit.git
synced 2024-11-24 11:45:26 +03:00
feat(orderBy): add handling one is a string and the other is a number case (#365)
* Add comparing string and number * Fix typo * fix testcase * Fix test case * simplify testcase
This commit is contained in:
parent
cc3a467443
commit
4af0662066
48
src/array/_internal/compareValues.spec.ts
Normal file
48
src/array/_internal/compareValues.spec.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { compareValues } from './compareValues';
|
||||
|
||||
describe('compareValues', () => {
|
||||
it('should return -1 if a < b', () => {
|
||||
expect(compareValues(1, 2, 'asc')).toBe(-1);
|
||||
});
|
||||
|
||||
it('should return 1 if a > b', () => {
|
||||
expect(compareValues(2, 1, 'asc')).toBe(1);
|
||||
});
|
||||
|
||||
it('should return 0 if a === b', () => {
|
||||
expect(compareValues(1, 1, 'asc')).toBe(0);
|
||||
});
|
||||
|
||||
it('should return 1 if a < b and order is desc', () => {
|
||||
expect(compareValues(1, 2, 'desc')).toBe(1);
|
||||
});
|
||||
|
||||
it('should return -1 if a > b and order is desc', () => {
|
||||
expect(compareValues(2, 1, 'desc')).toBe(-1);
|
||||
});
|
||||
|
||||
it('should return 0 if a === b and order is desc', () => {
|
||||
expect(compareValues(1, 1, 'desc')).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle the case where a is string and b is number', () => {
|
||||
expect(compareValues('0', 1, 'asc')).toBe(-1);
|
||||
expect(compareValues('a', 1, 'asc')).toBe(1);
|
||||
expect(compareValues('1b', 1, 'asc')).toBe(1);
|
||||
expect(compareValues('3', 1, 'asc')).toBe(1);
|
||||
expect(compareValues('2', 1, 'asc')).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle the case where a is number and b is string', () => {
|
||||
expect(compareValues(1, '0', 'asc')).toBe(1);
|
||||
expect(compareValues(1, 'a', 'asc')).toBe(-1);
|
||||
expect(compareValues(1, '1b', 'asc')).toBe(-1);
|
||||
expect(compareValues(1, '3', 'asc')).toBe(-1);
|
||||
expect(compareValues(1, '2', 'asc')).toBe(-1);
|
||||
});
|
||||
|
||||
it('should return 0 if a and b are not comparable', () => {
|
||||
expect(compareValues({ a: 'number' }, 1, 'asc')).toBe(0);
|
||||
});
|
||||
});
|
46
src/array/_internal/compareValues.ts
Normal file
46
src/array/_internal/compareValues.ts
Normal file
@ -0,0 +1,46 @@
|
||||
type Order = 'asc' | 'desc';
|
||||
|
||||
/**
|
||||
* Compares two values and returns a number indicating their order.
|
||||
*
|
||||
* This function can handle the following cases:
|
||||
* - If types of `a` and `b` are the same
|
||||
* - If types of `a` and `b` are not the same, but can convert to numbers(result is not NaN)
|
||||
* - If types of `a` and `b` are not the same, but one of them is a string and the other is a number
|
||||
*
|
||||
* If none of the above cases are met, it returns 0.
|
||||
*
|
||||
* @param {any} a - The first value to compare.
|
||||
* @param {any} b - The second value to compare.
|
||||
* @param {Order} order - The order direction ('asc' for ascending or 'desc' for descending).
|
||||
* @returns {number} - A number indicating the order of the two values.
|
||||
*/
|
||||
export function compareValues(a: any, b: any, order: Order) {
|
||||
// If type of a and b are the same or they can be converted to numbers(not NaN), compare them.
|
||||
if (a === b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a < b) {
|
||||
return order === 'desc' ? 1 : -1;
|
||||
}
|
||||
|
||||
if (a > b) {
|
||||
return order === 'desc' ? -1 : 1;
|
||||
}
|
||||
|
||||
// Type of a and b are not the same.
|
||||
// We only handle the case where a and b is string or number.
|
||||
if (typeof a === 'string' && typeof b === 'number') {
|
||||
b = b.toString();
|
||||
return compareValues(a, b, order);
|
||||
}
|
||||
|
||||
if (typeof a === 'number' && typeof b === 'string') {
|
||||
a = a.toString();
|
||||
return compareValues(a, b, order);
|
||||
}
|
||||
|
||||
// If a and b are not comparable, return 0
|
||||
return 0;
|
||||
}
|
@ -73,4 +73,61 @@ describe('orderBy', () => {
|
||||
{ user: 'fred', age: 48 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should order object has mixed value that is string and number', () => {
|
||||
const actual1 = orderBy(
|
||||
[
|
||||
{ id: 1, value: 'a' },
|
||||
{ id: 2, value: 2 },
|
||||
{ id: 12, value: 1 },
|
||||
{ id: 5, value: 'b' },
|
||||
{ id: 4, value: 2 },
|
||||
{ id: 43, value: 'c' },
|
||||
{ id: 24, value: 3 },
|
||||
{ id: 3, value: '3a' },
|
||||
{ id: 6, value: '2a' },
|
||||
{ id: 7, value: '1cs' },
|
||||
],
|
||||
['value', 'id'],
|
||||
['asc', 'asc']
|
||||
);
|
||||
const expected1 = [
|
||||
{ id: 12, value: 1 },
|
||||
{ id: 7, value: '1cs' },
|
||||
{ id: 2, value: 2 },
|
||||
{ id: 4, value: 2 },
|
||||
{ id: 6, value: '2a' },
|
||||
{ id: 24, value: 3 },
|
||||
{ id: 3, value: '3a' },
|
||||
{ id: 1, value: 'a' },
|
||||
{ id: 5, value: 'b' },
|
||||
{ id: 43, value: 'c' },
|
||||
];
|
||||
|
||||
const actual2 = orderBy(
|
||||
[
|
||||
{ id: 1, value: 'apple' },
|
||||
{ id: 2, value: 'banana' },
|
||||
{ id: 3, value: 'cherry' },
|
||||
{ id: 4, value: 10 },
|
||||
{ id: 5, value: 5 },
|
||||
{ id: 6, value: 'banana' },
|
||||
{ id: 7, value: 20 },
|
||||
],
|
||||
['value', 'id'],
|
||||
['asc', 'asc']
|
||||
);
|
||||
const expected2 = [
|
||||
{ id: 5, value: 5 },
|
||||
{ id: 4, value: 10 },
|
||||
{ id: 7, value: 20 },
|
||||
{ id: 1, value: 'apple' },
|
||||
{ id: 2, value: 'banana' },
|
||||
{ id: 6, value: 'banana' },
|
||||
{ id: 3, value: 'cherry' },
|
||||
];
|
||||
|
||||
expect(actual1).toEqual(expected1);
|
||||
expect(actual2).toEqual(expected2);
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { compareValues } from './_internal/compareValues';
|
||||
|
||||
type Order = 'asc' | 'desc';
|
||||
|
||||
/**
|
||||
@ -32,16 +34,6 @@ type Order = 'asc' | 'desc';
|
||||
* // ]
|
||||
*/
|
||||
export function orderBy<T>(collection: T[], keys: Array<keyof T>, orders: Order[]): T[] {
|
||||
const compareValues = (a: T[keyof T], b: T[keyof T], order: Order) => {
|
||||
if (a < b) {
|
||||
return order === 'asc' ? -1 : 1;
|
||||
}
|
||||
if (a > b) {
|
||||
return order === 'asc' ? 1 : -1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const effectiveOrders = keys.map((_, index) => orders[index] || orders[orders.length - 1]);
|
||||
|
||||
return collection.slice().sort((a, b) => {
|
||||
@ -49,6 +41,7 @@ export function orderBy<T>(collection: T[], keys: Array<keyof T>, orders: Order[
|
||||
const key = keys[i];
|
||||
const order = effectiveOrders[i];
|
||||
const result = compareValues(a[key], b[key], order);
|
||||
|
||||
if (result !== 0) {
|
||||
return result;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user