[[You-Dont-Need-Lodash-Underscore]]のサンプルコードをベースに[[TypeScript]]の型定義を追加したリスト。一部オリジナルのコードもあり。 ## mapValues ```ts export const mapValues = <K, T, U>( obj: { [key: string]: T }, to: (x: T) => U ): { [key: string]: U } => Object.fromEntries( Object.entries(obj).map(([key, value]) => [key, to(value)]) ); ``` ## mapKeys ```ts export const mapKeys = <T>( obj: { [key: string]: T }, to: (x: string) => string ): { [key: string]: T } => Object.fromEntries( Object.entries(obj).map(([key, value]) => [to(key), value]) ); ``` ## groupBy ```ts export const groupBy = <T>( values: T[], toKey: (t: T) => string, ): { [key: string]: T[] } => { const grouped: { [key: string]: T[] } = {}; for (const value of values) { const key = toKey(value); if (!grouped[key]) { grouped[key] = []; } grouped[key].push(value); } return grouped; }; ``` ### テストコード ```ts test.each< [ Parameters<typeof groupBy<string>>[0], Parameters<typeof groupBy<string>>[1], ReturnType<typeof groupBy<string>>, ] >([ [ ["a", "bb", "cc", "d"], (x) => x.length.toString(), { "1": ["a", "d"], "2": ["bb", "cc"] }, ], ])(`groupBy("%s")`, (values, toKey, expected) => { expect(groupBy(values, toKey)).toEqual(expected); }); ``` ## keyBy ```ts export const keyBy = <T>( values: T[], toKey: (t: T) => string, ): { [key: string]: T } => { const indexing: { [key: string]: T } = {}; for (const value of values) { const key = toKey(value); indexing[key] = value; } return indexing; }; ``` ## max ```ts export const max = (collection: number[]): number => { const select = (a: number, b: number) => (a >= b ? a : b) return collection.reduce(select) } ``` ## maxBy ```ts export const maxBy = <T>(collection: T[], toNum: (t: T) => number) => { const select = (a: T, b: T) => (toNum(a) >= toNum(b) ? a : b) return collection.reduce(select) } ``` `reduce`を使ってメソッドチェーンを使いたい場合は`reducer`を定義する方がCOOL! ```ts export const maxReducer = <T>(toNum: (t: T) => number) => { return (a: T, b: T) => (toNum(a) >= toNum(b) ? a : b) } export const maxReducerNum = maxReducer((x: number) => x) ``` ## minBy ```typescript export const minBy = <T>(collection: T[], toNum: (t: T) => number) => { const select = (a: T, b: T) => (toNum(a) <= toNum(b) ? a : b) return collection.reduce(select) } ``` ## uniq ```ts export function uniq<T>(values: T[]): T[] { return [...new Set(values)]; } ``` ## uniqBy ```ts export function uniqBy<T>(values: T[], fn: (x: T) => string | number): T[] { const m = new Map<string | number, T>(); for (const x of values) { const k = fn(x); if (!m.has(k)) { m.set(k, x); } } return Array.from(m.values()); } ``` ```ts test.each([ [["abc", "efg", "hijkl", "mnopq"], (x: string) => x.length, ["abc", "hijkl"]], ])( `uniqBy("%s", ...)`, ( values: Parameters<typeof uniqBy<string>>[0], fn: Parameters<typeof uniqBy<string>>[1], expected: ReturnType<typeof uniqBy<string>>, ) => { expect(uniqBy(values, fn)).toStrictEqual(expected); }, ); ``` ## strictUniq(オリジナル) ```ts /** * 基本はuniqと同じです。異なる点は配列やObjectが値として同一とみなせる場合で * - uniq: false * - strictUniq: true * をそれぞれ返します */ export function strictUniq<T>(values: T[]): T[] { return uniqBy(values, JSON.stringify); } ``` ```ts test.each([ [ [1, 2, 1], [1, 2], ], [[{ key: "a" }, { key: "a" }], [{ key: "a" }]], [ [ [1, 2], [1, 2], ], [[1, 2]], ], ])( `strictUniq("%s")`, ( values: Parameters<typeof strictUniq>[0], expected: ReturnType<typeof strictUniq>, ) => { expect(strictUniq(values)).toStrictEqual(expected); }, ); ``` ## uniqWith ```ts export function uniqWith<T>(arr: T[], fn: (one: T, other: T) => boolean) { return arr.filter( (element, index) => arr.findIndex((step) => fn(element, step)) === index ); } ``` ## count ```ts export const count = (values: string[]): { [value: string]: number } => { const ret: { [value: string]: number } = {}; for (const value of values) { if (ret[value]) { ret[value]++; } else { ret[value] = 1; } } return ret; }; ``` ## countBy ```ts ``` ## orderBy `sorter`を作る。 ```ts function sorter<T, U extends number | string>( toOrdered: (t: T) => U, order: "asc" | "desc" = "asc" ) { return (a: T, b: T) => order === "asc" ? toOrdered(a) > toOrdered(b) ? 1 : toOrdered(b) > toOrdered(a) ? -1 : 0 : toOrdered(a) < toOrdered(b) ? 1 : toOrdered(b) < toOrdered(a) ? -1 : 0; }; ``` 標準の`sort`で`sorter`を使う。 ```ts const res = [{id: 1, name: "ichi"}, {id: 3, name: "sun"}, {id: 2, name: "nii"}].sort(sorter((x) => x.id, 'desc')) ``` ```console:結果 [{ "id": 3, "name": "sun" }, { "id": 2, "name": "nii" }, { "id": 1, "name": "ichi" }] ``` - [Playground](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAZzgJygUzQHgCoA0iAqopgB5ZgAmyiYIAtgEbaIA+KUaMYA5gD4AFAChEiKHADyaatkzUAXIiFRleAJSIAvAJIExidHLTKARAENkEMx0Rm5129vtWbIrQG9DaTFBBoSEIW6kTM6lq6huLGbNrxrk7R4ogA-BLSsvLUwVp6kjImCkLMGskpaYgAjOUpygVZvjmliPmZRTkWZRUV6QC0NT11iAAMtfXt2bmIOBmFU6W16YM9E-NNJVqzDR25S4gD46MA3CIAvqciEAjIUIi+dC4A2p4wStVEYBaMmOYwEAALGBmM5EV7vADMn2+v3syHAILBb2UACZoT9zGAYMCzgBdAB0qAwQiJWDQQiE5EienI+LeRAA5I4IAyNGVrmBUAAbTD4rlwPhCB5lIA) ### テストコード ```ts const asIs = (x: any) => x; const ASC = "asc" as const; const DESC = "desc" as const; test.each([ // Asc [[2, 1], asIs, ASC, [1, 2]], [[1, 2], asIs, ASC, [1, 2]], [["z", "a"], asIs, ASC, ["a", "z"]], [["a", "z"], asIs, ASC, ["a", "z"]], // Desc [[1, 2], asIs, DESC, [2, 1]], [[2, 1], asIs, DESC, [2, 1]], [["a", "z"], asIs, DESC, ["z", "a"]], [["z", "a"], asIs, DESC, ["z", "a"]], // predicate [["aaa", "bb", "c"], (x) => x.length, ASC, ["c", "bb", "aaa"]], [["c", "bb", "aaa"], (x) => x.length, ASC, ["c", "bb", "aaa"]], ])( `orderBy("%s")`, ( collection: any[], predicate: (t: any) => any, order: "asc" | "desc", expected: ReturnType<typeof orderBy>, ) => { expect(orderBy(collection, predicate, order)).toEqual(expected); }, ); ``` ## arrayEquals ```ts export function arrayEquals( arr1: unknown[], arr2: unknown[], length?: number ): boolean { let l = Math.max(arr1.length, arr2.length); if (length !== undefined) { l = Math.min(l, length); } for (let i = 0; i < l; i++) { if (arr1[i] !== arr2[i]) { return false; } } return true; } ``` ## arrayEqualsUntil ```ts export function arrayEqualsUntil(arr1: unknown[], arr2: unknown[]): number { let l = Math.min(arr1.length, arr2.length); for (let i = 0; i < l; i++) { if (arr1[i] !== arr2[i]) { return i - 1; } } return l - 1; } ``` ## setEquals ```ts export function setEquals(set1: Set<unknown>, set2: Set<unknown>): boolean { if (set1.size !== set2.size) { return false; } return Array.from(set1).every((element) => set2.has(element)); } ``` ## intersection ```ts export function intersection<T>(matrix: T[][]): T[] { return matrix.length === 0 ? [] : matrix.reduce((acc, xs) => acc.filter((x) => xs.includes(x))); } ``` ## intersectionMap ```ts export function intersectionMap<T, U>(values: T[], mapper: (x: T) => U[]): U[] { return intersection(values.map(mapper)); } ``` ## omit ```ts export function omit<T extends { [key: string]: any }, K extends keyof T>( obj: T, keys: K[] ): Omit<T, K> { const cloned = { ...obj }; keys.forEach((k) => { delete cloned[k]; }); return cloned; } ``` ## omitBy ```ts export function omitBy<T extends { [key: string]: any }>( obj: T, shouldOmit: (key: string, value: any) => boolean, ): T { const cloned = { ...obj }; for (const [k, v] of Object.entries(cloned)) { if (shouldOmit(k, v)) { delete cloned[k]; } } return cloned; } ``` ### テストコード ```ts test.each< [ Parameters<typeof omitBy>[0], Parameters<typeof omitBy>[1], ReturnType<typeof omitBy>, ] >([ [{ a: 1, b: 2, c: 3 }, (_, v) => v > 1, { a: 1 }], [{ a: 1, b: 2, c: 3 }, (k, _) => k === "c", { a: 1, b: 2 }], ])(`omitBy("%s")`, (collection, shouldOmit, expected) => { expect(omitBy(collection, shouldOmit)).toEqual(expected); }); ``` ## chunk ```ts export const chunk = <T>(values: T[], size: number): T[][] => values.reduce<T[][]>( (arr, item, idx) => idx % size === 0 ? [...arr, [item]] : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]], [] ); ``` ## zip ```ts export function zip<T, U>(xs: T[], ys: U[]): [T, U][] { return Array(Math.max(xs.length, ys.length)) .fill("") .map((_, i) => [xs[i], ys[i]]); } ``` ## fromPairs ```ts export function fromPairs<V>(arr: [string, V][]): { [key: string]: V } { return arr.reduce((accumulator: any, value: any) => { accumulator[value[0]] = value[1]; return accumulator; }, {}); } ``` ## clamp ```ts export function clamp(value: number, min: number, max: number): number { return Math.min(Math.max(value, min), max); } ``` ## range ```ts export function range(n: number): number[] { return [...Array(n).keys()]; } ``` ## duplicateObject (オリジナル) オブジェクトのclone. ```ts function duplicateObject<T>(obj: T, count: number): T[] { return Array.from({ length: count }, () => ({ ...obj })); } ```