diff --git a/libs/dfts-helper/src/index.ts b/libs/dfts-helper/src/index.ts index 49ae5a63..fa4c70b9 100644 --- a/libs/dfts-helper/src/index.ts +++ b/libs/dfts-helper/src/index.ts @@ -23,7 +23,9 @@ export { a_pushIf } from './lib/helper/array/push-if'; export { a_pushIfAbsent } from './lib/helper/array/push-if-absent/push-if-absent'; export { a_remove } from './lib/helper/array/remove/remove'; export { a_removeIf } from './lib/helper/array/remove-if'; +export { a_chunk } from './lib/helper/array/chunk/chunk'; export { a_shuffle } from './lib/helper/array/shuffle'; +export { a_hashFrom } from './lib/helper/array/hash/hash'; export { b_from } from './lib/helper/boolean/from/from'; export { b_fromStorage } from './lib/helper/boolean/from-storage/from-storage'; diff --git a/libs/dfts-helper/src/lib/helper/array/chunk/chunk.spec.ts b/libs/dfts-helper/src/lib/helper/array/chunk/chunk.spec.ts new file mode 100644 index 00000000..a12393c1 --- /dev/null +++ b/libs/dfts-helper/src/lib/helper/array/chunk/chunk.spec.ts @@ -0,0 +1,138 @@ +import { a_chunk } from './chunk'; + +describe('a_chunk', () => { + test('splits an evenly divisible array correctly', () => { + const input = [1, 2, 3, 4, 5, 6]; + const result = a_chunk(input, 2); + expect(result).toEqual([ + [1, 2], + [3, 4], + [5, 6], + ]); + }); + + test('handles chunk size of 1', () => { + const input = [1, 2, 3]; + const result = a_chunk(input, 1); + expect(result).toEqual([[1], [2], [3]]); + }); + + test('returns empty array when given an empty array', () => { + const input: number[] = []; + const result = a_chunk(input, 2); + expect(result).toEqual([]); + }); + + test('returns one chunk if chunk size equals the entire array length', () => { + const input = [1, 2, 3, 4]; + const result = a_chunk(input, 4); + expect(result).toEqual([[1, 2, 3, 4]]); + }); + + test('last chunk may be smaller if array length is not divisible by chunk size', () => { + const input = [1, 2, 3, 4, 5]; + const result = a_chunk(input, 2); + expect(result).toEqual([[1, 2], [3, 4], [5]]); + }); + + test('handles chunk size larger than the array length', () => { + const input = [1, 2, 3]; + const result = a_chunk(input, 10); + expect(result).toEqual([[1, 2, 3]]); + }); + + test('works with different data types (strings)', () => { + const input = ['a', 'b', 'c', 'd']; + const result = a_chunk(input, 2); + expect(result).toEqual([ + ['a', 'b'], + ['c', 'd'], + ]); + }); + + test('works with mixed data types', () => { + const input = [1, 'two', { three: 3 }, [4], null]; + const result = a_chunk(input, 2); + expect(result).toEqual([[1, 'two'], [{ three: 3 }, [4]], [null]]); + }); + + test('handles a chunk size of 3', () => { + const input = [1, 2, 3, 4, 5, 6, 7]; + const result = a_chunk(input, 3); + expect(result).toEqual([[1, 2, 3], [4, 5, 6], [7]]); + }); + + test('does not modify the original array', () => { + const input = [1, 2, 3, 4, 5]; + const copy = [...input]; + a_chunk(input, 2); + expect(input).toEqual(copy); + }); + + test('handles a large array efficiently', () => { + const input = Array.from({ length: 1000 }, (_, i) => i + 1); + const result = a_chunk(input, 100); + expect(result.length).toBe(10); + expect(result[0].length).toBe(100); + }); + + test('handles chunk size equal to 2 with odd length input', () => { + const input = [1, 2, 3]; + const result = a_chunk(input, 2); + expect(result).toEqual([[1, 2], [3]]); + }); + + test('handles chunk size equal to the first half of array length', () => { + const input = [1, 2, 3, 4, 5, 6]; + const result = a_chunk(input, 3); + expect(result).toEqual([ + [1, 2, 3], + [4, 5, 6], + ]); + }); + + test('works with a single-element array', () => { + const input = [42]; + const result = a_chunk(input, 5); + expect(result).toEqual([[42]]); + }); + + test('works with an array of booleans', () => { + const input = [true, false, true, false, true]; + const result = a_chunk(input, 2); + expect(result).toEqual([[true, false], [true, false], [true]]); + }); + + test('works with nested arrays as elements', () => { + const input = [[1], [2], [3], [4]]; + const result = a_chunk(input, 2); + expect(result).toEqual([ + [[1], [2]], + [[3], [4]], + ]); + }); + + test('handles chunk size of 1 on large input', () => { + const input = [1, 2, 3, 4, 5, 6]; + const result = a_chunk(input, 1); + expect(result).toEqual([[1], [2], [3], [4], [5], [6]]); + }); + + test('returns the input if chunk size is equal to input length', () => { + const input = [10, 20, 30]; + const result = a_chunk(input, 3); + expect(result).toEqual([[10, 20, 30]]); + }); + + test('works with chunk size of 2 on an empty array', () => { + const input: number[] = []; + const result = a_chunk(input, 2); + expect(result).toEqual([]); + }); + + test('handles large chunk size with large input', () => { + const input = Array.from({ length: 20 }, (_, i) => i + 1); + const result = a_chunk(input, 25); + expect(result).toEqual([input]); // entire array as one chunk + }); +}); diff --git a/libs/dfts-helper/src/lib/helper/array/chunk/chunk.ts b/libs/dfts-helper/src/lib/helper/array/chunk/chunk.ts new file mode 100644 index 00000000..b16d0d2a --- /dev/null +++ b/libs/dfts-helper/src/lib/helper/array/chunk/chunk.ts @@ -0,0 +1,9 @@ +export function a_chunk(input: T[], chunkSize: number): T[][] { + const result: T[][] = []; + + for (let i = 0; i < input.length; i += chunkSize) { + result.push(input.slice(i, i + chunkSize)); + } + + return result; +} diff --git a/libs/dfts-helper/src/lib/helper/array/hash/hash.spec.ts b/libs/dfts-helper/src/lib/helper/array/hash/hash.spec.ts new file mode 100644 index 00000000..414049ee --- /dev/null +++ b/libs/dfts-helper/src/lib/helper/array/hash/hash.spec.ts @@ -0,0 +1,115 @@ +import { a_hashFrom } from './hash'; + +describe('a_hashFrom', () => { + test('returns first element if the string is empty', () => { + const arr = ['a', 'b', 'c']; + expect(a_hashFrom(arr, '')).toBe('a'); + }); + + test('works with a single-character string', () => { + const arr = ['x', 'y', 'z']; + expect(a_hashFrom(arr, 'A')).toBeDefined(); + }); + + test('works with a multi-character string', () => { + const arr = ['apple', 'banana', 'cherry', 'date']; + const result = a_hashFrom(arr, 'hello'); + expect(arr).toContain(result); + }); + + test('returns a consistent value for the same string', () => { + const arr = [1, 2, 3, 4, 5]; + const str = 'consistent'; + const firstCall = a_hashFrom(arr, str); + const secondCall = a_hashFrom(arr, str); + expect(firstCall).toBe(secondCall); + }); + + test('different strings should produce different (not necessarily unique) results', () => { + const arr = [1, 2, 3, 4, 5]; + const result1 = a_hashFrom(arr, 'abc'); + const result2 = a_hashFrom(arr, 'abd'); + // They might map to the same index by chance, but often won't. + // This test just ensures the function runs without error. + expect(arr).toContain(result1); + expect(arr).toContain(result2); + }); + + test('works with non-string array elements (numbers)', () => { + const arr = [10, 20, 30]; + const result = a_hashFrom(arr, 'test'); + expect([10, 20, 30]).toContain(result); + }); + + test('works with boolean elements', () => { + const arr = [true, false]; + const result = a_hashFrom(arr, 'true-or-false'); + expect([true, false]).toContain(result); + }); + + test('works with objects', () => { + const arr = [{ id: 1 }, { id: 2 }, { id: 3 }]; + const result = a_hashFrom(arr, 'object-test'); + expect(arr).toContain(result); + }); + + test('works with large arrays', () => { + const arr = Array.from({ length: 100 }, (_, i) => i); + const result = a_hashFrom(arr, 'large-array'); + expect(arr).toContain(result); + }); + + test('handles special characters in the string', () => { + const arr = ['A', 'B', 'C', 'D']; + const result = a_hashFrom(arr, '✨unicode✨'); + expect(arr).toContain(result); + }); + + test('handles strings that differ by only one character', () => { + const arr = ['red', 'green', 'blue']; + const result1 = a_hashFrom(arr, 'colorA'); + const result2 = a_hashFrom(arr, 'colorB'); + expect(arr).toContain(result1); + expect(arr).toContain(result2); + // Just ensuring both run without errors and produce valid outputs. + }); + + test('works with numeric strings', () => { + const arr = [null, 'second', 'third']; + const result = a_hashFrom(arr, '12345'); + expect([null, 'second', 'third']).toContain(result); + }); + + test('works with very long strings', () => { + const arr = ['first', 'second', 'third']; + const longStr = 'a'.repeat(10_000); + const result = a_hashFrom(arr, longStr); + expect(arr).toContain(result); + }); + + test('works with chunked strings', () => { + const arr = ['first', 'second', 'third', 'fourth']; + const str = 'abcd'.repeat(100); // a repeated pattern + const result = a_hashFrom(arr, str); + expect(arr).toContain(result); + }); + + test('works with a single-element array', () => { + const arr = [42]; + const result = a_hashFrom(arr, 'anything'); + expect(result).toBe(42); + }); + + test('works with a prime-length array', () => { + const arr = [true, false, null, undefined, 0, '', 'end']; + // length: 7 (prime) + const result = a_hashFrom(arr, 'prime-length-test'); + expect(arr).toContain(result); + }); + + test('handles an array of strings with repeated elements', () => { + const arr = ['repeat', 'repeat', 'unique', 'repeat']; + const result = a_hashFrom(arr, 'repetitive'); + expect(arr).toContain(result); + }); +}); diff --git a/libs/dfts-helper/src/lib/helper/array/hash/hash.ts b/libs/dfts-helper/src/lib/helper/array/hash/hash.ts new file mode 100644 index 00000000..88c75d2a --- /dev/null +++ b/libs/dfts-helper/src/lib/helper/array/hash/hash.ts @@ -0,0 +1,11 @@ +export function a_hashFrom(array: T[], str: string): T { + const length = str.length; + if (length === 0) return array[0]; + + let hash = 0; + for (let i = 0; i < length; i++) { + hash = (hash * 31 + str.charCodeAt(i)) >>> 0; + } + + return array[hash % array.length]; +}