[[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 }));
}
```