perf(at): Improve performance and compatibility with lodash (#798)

* perf(at): Improve performance and compatibility with lodash

* Change the implementation to match `Array.prototype.at` implementation

* Avoid truncating numbers if they are already integers
This commit is contained in:
Filip Sobol 2024-11-10 07:10:03 +01:00 committed by GitHub
parent 894a645c49
commit c939d97322
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 35 additions and 10 deletions

View File

@ -6,24 +6,35 @@ const atToolkit = atToolkit_;
const atLodash = atLodash_; const atLodash = atLodash_;
describe('at', () => { describe('at', () => {
const data = ['a', 'b', 'c', 'd', 'e'];
const indices = [0, 2, -2, 1.5, -1.5];
bench('es-toolkit/at', () => { bench('es-toolkit/at', () => {
atToolkit(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]); atToolkit(data, indices);
}); });
bench('lodash/at', () => { bench('lodash/at', () => {
atLodash(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]); atLodash(data, indices);
});
bench('Array.prototype.at', () => {
indices.map(i => data.at(i));
}); });
}); });
describe('at/largeArray', () => { describe('at/largeArray', () => {
const largeArray = Array.from({ length: 10000 }, (_, i) => i); const largeData = Array.from({ length: 10000 }, (_, i) => i);
const largeIndex = Array.from({ length: 1000 }, (_, i) => i * 2); const largeIndices = Array.from({ length: 1000 }, (_, i) => i * 2);
bench('es-toolkit/at', () => { bench('es-toolkit/at', () => {
atToolkit(largeArray, largeIndex); atToolkit(largeData, largeIndices);
}); });
bench('lodash/at', () => { bench('lodash/at', () => {
atLodash(largeArray, largeIndex); atLodash(largeData, largeIndices);
});
bench('Array.prototype.at', () => {
largeIndices.map(i => largeData.at(i));
}); });
}); });

View File

@ -13,10 +13,17 @@ describe('at', () => {
}); });
it('should return `undefined` for nonexistent keys', () => { it('should return `undefined` for nonexistent keys', () => {
expect(at(['a', 'b', 'c'], [2, 4, 0])).toEqual(['c', undefined, 'a']); expect(at(['a', 'b', 'c'], [2, 4, 0, -4])).toEqual(['c', undefined, 'a', undefined]);
}); });
it('should return an empty array when no keys are given', () => { it('should return an empty array when no keys are given', () => {
expect(at(['a', 'b', 'c'], [])).toEqual([]); expect(at(['a', 'b', 'c'], [])).toEqual([]);
}); });
it('should return undefined for non-integer indices', () => {
const data = ['a', 'b', 'c'];
const indices = [1.5, -1.5, NaN, Infinity, -Infinity];
expect(at(data, indices)).toEqual(indices.map(i => data.at(i)));
});
}); });

View File

@ -14,12 +14,19 @@
* console.log(result); // [20, 40, 50] * console.log(result); // [20, 40, 50]
*/ */
export function at<T>(arr: readonly T[], indices: number[]): Array<T | undefined> { export function at<T>(arr: readonly T[], indices: number[]): Array<T | undefined> {
const result: Array<T | undefined> = new Array(indices.length); const result = new Array<T | undefined>(indices.length);
const length = arr.length;
for (let i = 0; i < indices.length; i++) { for (let i = 0; i < indices.length; i++) {
const index = indices[i]; let index = indices[i];
result[i] = arr.at(index); index = Number.isInteger(index) ? index : Math.trunc(index) || 0;
if (index < 0) {
index += length;
}
result[i] = arr[index];
} }
return result; return result;