Skip to content

Commit

Permalink
fix: incorrect sorting of numeric values with leading zeros and trail…
Browse files Browse the repository at this point in the history
…ing characters (#62)

* refactor(normalizechunk): separate numeric and alphanumeric logic

* fix: keep string value of numeric chunks

* fix: use createChunkMaps instead of createChunksList

* fix(comparechunks): compare string value, if numeric chunks are equal

* test: fix tests

* fix: add latest size snapshot
  • Loading branch information
yobacca committed Mar 19, 2019
1 parent 2a52912 commit 723abb4
Show file tree
Hide file tree
Showing 22 changed files with 486 additions and 216 deletions.
6 changes: 3 additions & 3 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"iife/natural-orderby.min.js": {
"bundled": 18376,
"minified": 4406,
"gzipped": 1554
"bundled": 18985,
"minified": 4723,
"gzipped": 1573
}
}
11 changes: 9 additions & 2 deletions src/types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@ export type Identifier<T> = IdentifierFn<T> | string;

export type ParsedNumber = number;

export type Chunk = string | number;
export type Chunk = string;

export type Chunks = $ReadOnlyArray<Chunk>;

export type ChunkMap = {
parsedNumber?: number,
normalizedString: string,
};

export type ChunkMaps = $ReadOnlyArray<ChunkMap>;

export type MappedValueRecord = {|
parsedNumber?: ParsedNumber,
chunks?: Chunks,
chunks?: ChunkMaps,
isArray?: boolean,
isFunction?: boolean,
isNaN?: boolean,
Expand Down
55 changes: 45 additions & 10 deletions src/utils/__tests__/baseCompare.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,35 @@ describe('baseCompare()', () => {
);
});
});
describe('numerics with prepended zero and optional trailing characters', () => {
const origArray = [
'05',
'06',
'07*',
'08A',
'09 B',
'10',
'11',
'12A',
'13',
];
const sortArray = [
'05',
'06',
'07*',
'08A',
'09 B',
'10',
'11',
'12A',
'13',
];
it(`${origArray.toString()} should be returned as ${sortArray.toString()}`, () => {
expect(origArray.sort(baseCompare({ order: 'asc' }))).toEqual(
sortArray
);
});
});
describe('negative numbers as strings - mixed input type, string + numeric', () => {
const origArray = [-1, '-2', 4, -3, '0', '-5'];
const sortArray = ['-5', -3, '-2', -1, '0', 4];
Expand Down Expand Up @@ -813,18 +842,24 @@ describe('baseCompare()', () => {
});
describe('complex filenames', () => {
const origArray = [
'car.mov',
'01alpha.sgi',
'001alpha.sgi',
'my.string_41299.tif',
'organic2.0001.sgi',
'01asset_0815.png',
'asset_47103.jpg',
'asset_151.jpg',
'asset_001.jpg',
'asset_0001.jpg',
'001asset_4711.jpg',
'0001asset_4711.jpg',
'asset_342.mp4',
];
const sortArray = [
'001alpha.sgi',
'01alpha.sgi',
'car.mov',
'my.string_41299.tif',
'organic2.0001.sgi',
'0001asset_4711.jpg',
'001asset_4711.jpg',
'01asset_0815.png',
'asset_0001.jpg',
'asset_001.jpg',
'asset_151.jpg',
'asset_342.mp4',
'asset_47103.jpg',
];
it(`${origArray.toString()} should be returned as ${sortArray.toString()}`, () => {
expect(origArray.sort(baseCompare({ order: 'asc' }))).toEqual(
Expand Down
33 changes: 33 additions & 0 deletions src/utils/__tests__/baseOrderBy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,33 @@ describe('baseOrderBy()', () => {
expect(baseOrderBy(origArray, [], [])).toEqual(sortArray);
});
});
describe('numerics with prepended zero and optional trailing characters', () => {
const origArray = [
'05',
'06',
'07*',
'08A',
'09 B',
'10',
'11',
'12A',
'13',
];
const sortArray = [
'05',
'06',
'07*',
'08A',
'09 B',
'10',
'11',
'12A',
'13',
];
it(`${origArray.toString()} should be returned as ${sortArray.toString()}`, () => {
expect(baseOrderBy(origArray, [], [])).toEqual(sortArray);
});
});
describe('negative numbers as strings - mixed input type, string + numeric', () => {
const origArray = [-1, '-2', 4, -3, '0', '-5'];
const sortArray = ['-5', -3, '-2', -1, '0', 4];
Expand Down Expand Up @@ -837,12 +864,18 @@ describe('baseOrderBy()', () => {
'01asset_0815.png',
'asset_47103.jpg',
'asset_151.jpg',
'asset_001.jpg',
'asset_0001.jpg',
'001asset_4711.jpg',
'0001asset_4711.jpg',
'asset_342.mp4',
];
const sortArray = [
'0001asset_4711.jpg',
'001asset_4711.jpg',
'01asset_0815.png',
'asset_0001.jpg',
'asset_001.jpg',
'asset_151.jpg',
'asset_342.mp4',
'asset_47103.jpg',
Expand Down
140 changes: 108 additions & 32 deletions src/utils/__tests__/compareChunks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,83 +4,159 @@ import compareChunks from '../compareChunks';

describe('compareChunks()', () => {
it('should return -1', () => {
const chunksA = [1, '.', 0, '.', 0];
const chunksB = [1, '.', 0, '.', 1];
const chunksA = [
{ parsedNumber: 1, normalizedString: '1' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
];
const chunksB = [
{ parsedNumber: 1, normalizedString: '1' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 1, normalizedString: '1' },
];
expect(compareChunks(chunksA, chunksB)).toBeLessThan(0);
});
it('should return 0', () => {
const chunksA = [1, '.', 0, '.', 0];
const chunksB = [1, '.', 0, '.', 0];
const chunksA = [
{ parsedNumber: 1, normalizedString: '1' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
];
const chunksB = [
{ parsedNumber: 1, normalizedString: '1' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
];
expect(compareChunks(chunksA, chunksB)).toBe(0);
});
it('should return 1', () => {
const chunksA = [1, '.', 0, '.', 1];
const chunksB = [1, '.', 0, '.', 0];
const chunksA = [
{ parsedNumber: 1, normalizedString: '1' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 1, normalizedString: '1' },
];
const chunksB = [
{ parsedNumber: 1, normalizedString: '1' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
];
expect(compareChunks(chunksA, chunksB)).toBeGreaterThan(0);
});
it('should return -1', () => {
const chunksA = [1, '.', 0, '.', 1];
const chunksB = [2, '.', 0, '.', 0];
const chunksA = [
{ parsedNumber: 1, normalizedString: '1' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 1, normalizedString: '1' },
];
const chunksB = [
{ parsedNumber: 2, normalizedString: '2' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
];
expect(compareChunks(chunksA, chunksB)).toBeLessThan(0);
});
it('should return -1', () => {
const chunksA = [1, '.', 0, '.', 0, '-alpha'];
const chunksB = [1, '.', 0, '.', 0];
const chunksA = [
{ parsedNumber: 1, normalizedString: '1' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '-alpha' },
];
const chunksB = [
{ parsedNumber: 1, normalizedString: '1' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
{ parsedNumber: undefined, normalizedString: '.' },
{ parsedNumber: 0, normalizedString: '0' },
];
expect(compareChunks(chunksA, chunksB)).toBeGreaterThan(0);
});
it('should return -1', () => {
const chunksA = [1];
const chunksB = ['x'];
const chunksA = [{ parsedNumber: 1, normalizedString: '1' }];
const chunksB = [{ parsedNumber: undefined, normalizedString: 'x' }];
expect(compareChunks(chunksA, chunksB)).toBeLessThan(0);
});
it('should return 1', () => {
const chunksA = ['x'];
const chunksB = [1];
const chunksA = [{ parsedNumber: undefined, normalizedString: 'x' }];
const chunksB = [{ parsedNumber: 1, normalizedString: '1' }];
expect(compareChunks(chunksA, chunksB)).toBeGreaterThan(0);
});
it('should return -1', () => {
const chunksA = ['ö'];
const chunksB = ['ü'];
const chunksA = [{ parsedNumber: undefined, normalizedString: 'ö' }];
const chunksB = [{ parsedNumber: undefined, normalizedString: 'ü' }];
expect(compareChunks(chunksA, chunksB)).toBeLessThan(0);
});
it('should return 0', () => {
const chunksA = ['ö'];
const chunksB = ['ö'];
const chunksA = [{ parsedNumber: undefined, normalizedString: 'ö' }];
const chunksB = [{ parsedNumber: undefined, normalizedString: 'ö' }];
expect(compareChunks(chunksA, chunksB)).toBe(0);
});
it('should return 1', () => {
const chunksA = ['ü'];
const chunksB = ['ö'];
const chunksA = [{ parsedNumber: undefined, normalizedString: 'ü' }];
const chunksB = [{ parsedNumber: undefined, normalizedString: 'ö' }];
expect(compareChunks(chunksA, chunksB)).toBeGreaterThan(0);
});
it('should return -1', () => {
const chunksA = ['a'];
const chunksB = ['b'];
const chunksA = [{ parsedNumber: undefined, normalizedString: 'a' }];
const chunksB = [{ parsedNumber: undefined, normalizedString: 'b' }];
expect(compareChunks(chunksA, chunksB)).toBeLessThan(0);
});
it('should return 0', () => {
const chunksA = ['a'];
const chunksB = ['a'];
const chunksA = [{ parsedNumber: undefined, normalizedString: 'a' }];
const chunksB = [{ parsedNumber: undefined, normalizedString: 'a' }];
expect(compareChunks(chunksA, chunksB)).toBe(0);
});
it('should return 1', () => {
const chunksA = ['b'];
const chunksB = ['a'];
const chunksA = [{ parsedNumber: undefined, normalizedString: 'b' }];
const chunksB = [{ parsedNumber: undefined, normalizedString: 'a' }];
expect(compareChunks(chunksA, chunksB)).toBeGreaterThan(0);
});
it('should return -1', () => {
const chunksA = [''];
const chunksB = [0];
const chunksA = [{ parsedNumber: undefined, normalizedString: '' }];
const chunksB = [{ parsedNumber: 0, normalizedString: '0' }];
expect(compareChunks(chunksA, chunksB)).toBeLessThan(0);
});
it('should return 0', () => {
const chunksA = [''];
const chunksB = [''];
const chunksA = [{ parsedNumber: undefined, normalizedString: '' }];
const chunksB = [{ parsedNumber: undefined, normalizedString: '' }];
expect(compareChunks(chunksA, chunksB)).toBe(0);
});
it('should return 1', () => {
const chunksA = [0];
const chunksB = [''];
const chunksA = [{ parsedNumber: 0, normalizedString: '0' }];
const chunksB = [{ parsedNumber: undefined, normalizedString: '' }];
expect(compareChunks(chunksA, chunksB)).toBeGreaterThan(0);
});
it('should return -1', () => {
const chunksA = [{ parsedNumber: 1, normalizedString: '001' }];
const chunksB = [{ parsedNumber: 1, normalizedString: '01' }];
expect(compareChunks(chunksA, chunksB)).toBeLessThan(0);
});
it('should return -1', () => {
const chunksA = [{ parsedNumber: 1, normalizedString: '001' }];
const chunksB = [{ parsedNumber: 1, normalizedString: '001' }];
expect(compareChunks(chunksA, chunksB)).toBe(0);
});
it('should return 1', () => {
const chunksA = [{ parsedNumber: 1, normalizedString: '01' }];
const chunksB = [{ parsedNumber: 1, normalizedString: '001' }];
expect(compareChunks(chunksA, chunksB)).toBeGreaterThan(0);
});
});

0 comments on commit 723abb4

Please sign in to comment.