/** * リストに区切りの要素を追加する */ export const listAddSeparator = ( list: ReadonlyArray, separator: Element ): ReadonlyArray => { const first = list[0]; if (first === undefined) { return []; } return [ first, ...list .slice(1) .flatMap((element): ReadonlyArray => [separator, element]), ]; }; /** * `undefined` かもしれない値に関数を呼ぶ */ export const maybeMap = ( value: Input | undefined, func: (input: Input) => Output ): Output | undefined => { if (value === undefined) { return undefined; } return func(value); }; /** * 指定したインデックスの要素を削除した配列を生成する * @example * listDeleteAt(["a", "b", "c", "d"], 2) // ["a", "b", "d"] */ export const listDeleteAt = ( list: ReadonlyArray, index: number ): ReadonlyArray => { return [ ...list.slice(0, Math.max(0, index)), ...list.slice(Math.max(0, index + 1)), ]; }; export const listUpdateAt = ( list: ReadonlyArray, index: number, func: (e: Element) => Element ): ReadonlyArray => { const element = list[index]; if (element === undefined) { return list; } return [ ...list.slice(0, Math.max(0, index)), func(element), ...list.slice(Math.max(0, index + 1)), ]; }; export const listUpdateAtOverAutoCreate = ( list: ReadonlyArray, index: number, func: (e: Element | undefined) => Element, fillElement: Element ): ReadonlyArray => { const element = list[index]; if (element === undefined) { if (list.length <= index) { return [ ...list, ...new Array(index - list.length).fill(fillElement), func(undefined), ]; } return list; } return [ ...list.slice(0, Math.max(0, index)), func(element), ...list.slice(Math.max(0, index + 1)), ]; }; export const listSetAt = ( list: ReadonlyArray, index: number, newElement: Element ): ReadonlyArray => { if (index < 0 || list.length <= index) { return list; } return [ ...list.slice(0, Math.max(0, index)), newElement, ...list.slice(Math.max(0, index + 1)), ]; }; export const neverFunc = (): never => { throw new Error("呼ばれないはずの関数. neverFunc が呼ばれた!"); }; /** * 配列をグループ分けする * https://qiita.com/nagtkk/items/e1cc3f929b61b1882bd1 * @param list グループ分けする配列 * @param groupIndexFunc グループ番号(外側の配列のインデックス)を返す関数 * * @example * scatter(["a", "bb", "c", "ddd"], (text)=>text.length) // [[],["a", "c"], ["bb"], ["ddd"]] */ export const group = ( list: ReadonlyArray, groupIndexFunc: (element: T, index: number) => number ): ReadonlyArray> => list.reduce>>( (result, cur, index): ReadonlyArray> => { return listUpdateAtOverAutoCreate>( result, groupIndexFunc(cur, index), (item) => [...(item ?? []), cur], [] ); }, [] ); export const groupBySize = ( list: ReadonlyArray, size: number ): ReadonlyArray> => { if (size < 0) { throw new Error("size need +"); } return group(list, (_, i) => Math.floor(i / Math.floor(size))); }; /** * ランダムな ID の文字列を生成する * [crypto.getRandomValues()](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues) を使っているので, * ブラウザか, Node.js v15 以降 の環境でのみ動く * @returns */ export const createRandomId = (): string => { const binary = crypto.getRandomValues(new Uint8Array(32)); let result = ""; for (const item of binary) { result += item.toString(16).padStart(2, "0"); } return result; };