【问题标题】:The proper fp-ts way to convert array into object将数组转换为对象的正确 fp-ts 方法
【发布时间】:2021-07-01 21:05:45
【问题描述】:

我是函数式编程的新手,但想学习最佳实践。

将数组转换为对象的正确 fp-ts 方法是什么?

(items: Item[], keyGetter: (i: Item) => Key) => Record<Key, Item>

到目前为止,我使用自己的非 fp-ts 实现:

function isDefined<T>(value: T): value is Exclude<T, undefined> {
  return value !== undefined;
}

type TIdentifier = string | number;

export const arrayToRecord = <T1, T2 extends TIdentifier = string>(
  arr: T1[],
  getKeyName?: (item: T1) => T2
): Record<T2, T1> => {
  const hasKeyNameGetter = isDefined(getKeyName);
  return arr.reduce((acc, item) => {
    acc[
      hasKeyNameGetter ? (getKeyName as (item: T1) => T2)(item) : ((item as unknown) as T2)
    ] = item;
    return acc;
  }, {} as Record<T2, T1>);
};

【问题讨论】:

  • 请提供可重现的例子
  • 什么例子?我的 arrayToRecord 函数的 fp-ts 等效?这就是IDK如何实现的东西????
  • TIdentifier 示例,isDEfined。您的代码不可重现
  • 我明白了,对不起。我已经扩展了我的例子????
  • item as unknown as T2 不是很实用:-) 如果你真的需要这个,我建议你重载你的函数为(items: Key[]) =&gt; Record&lt;Key, Key&gt;

标签: typescript functional-programming fp-ts


【解决方案1】:

这是实现您所要求的一种方法。

一些注意事项:

  • 由于字典是在运行时构建的并且没有对键的保证,为了防止不安全的代码返回类型是Record&lt;string, A&gt;
  • keyGetter不能是可选的,我们必须提供一种方法来想出e键
import * as A from 'fp-ts/ReadonlyArray'
import * as R from 'fp-ts/ReadonlyRecord'
import { pipe } from 'fp-ts/function'

const arrayToRecord = <A>(
  items: ReadonlyArray<A>,
  keyGetter: (i: A) => string,
): Readonly<Record<string, A>> =>
  pipe(
    items,
    A.reduce({}, (acc, item) => pipe(acc, R.upsertAt(keyGetter(item), item))),
  )

编辑

请求的示例:

const xs = [
  { id: 'abc', date: new Date() },
  { id: 'snt', date: new Date() },
]
const res = arrayToRecord(xs, (x) => x.id)

console.log(res)
// {
//   abc: { id: 'abc', date: 2021-04-06T13:09:25.732Z },
//   snt: { id: 'snt', date: 2021-04-06T13:09:25.732Z }
// }

编辑 2

pipe友好版:

declare const arrayToRecord: <A>(
  keyGetter: (i: A) => string,
) => (items: ReadonlyArray<A>) => Readonly<Record<string, A>>

interface X { id: string; date: Date }

declare const xs: ReadonlyArray<X>

pipe(
  xs,
  arrayToRecord((x) => x.id),
) // Readonly<Record<string, X>>

【讨论】:

  • 谢谢!您能否用用例扩展您的答案,例如将此功能投入使用。假设我们有items数组和keyGetter fn,那么如何以pipe fp-ts方式放入?
  • @DmitriiBykov 我已经添加了。
  • 我可以看到您的编辑,但我的意思是另一回事。如何在管道和 fp-ts 的其他东西中使用它?
  • 如果您希望该功能“管道友好”,您必须将其类型签名更改为 &lt;A&gt;(keyGetter: (i: A) =&gt; string) =&gt; (items: ReadonlyArray&lt;A&gt;) =&gt; Readonly&lt;Record&lt;string, A&gt;&gt; 用法:pipe(xs, arrayToRecord((x) =&gt; x.id))
  • 我看不出有什么方法可以得到这个pipe(xs, keyGetter, arrayToRecord)(而且我实际上不明白你为什么要做这个练习)。此外,fp-ts 充满了高阶函数,所以你通常会以这个结尾,例如:pipe(O.some(1), O.map(n =&gt; n + 1), O.chain((n =&gt; n % 2 == 0 ? O.some(n) : O.none))
【解决方案2】:

这是唯一的fp-ts 解决方案:

import * as A from "fp-ts/lib/Array";
import * as R from "fp-ts/lib/Record";
import * as semigroup from 'fp-ts/Semigroup';

const arr = A.fromArray([1,2,3]);

const testRecord = R.fromFoldableMap(
  semigroup.last<number>(),
  A.array
)(arr, key => [String(key), key]);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-10
    • 2019-01-26
    • 2014-11-06
    • 2021-10-20
    • 2019-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多