如何fold/reduce一张地图?
How to fold/reduce a map?
我希望能够 fold/reduce 一个 Map,就像我可以使用 Array 和 Set 一样。我看到的最接近的是一个叫做 getFoldableWithIndex
的东西,但我不知道如何使用它或让它与 Typescript 一起编译。我觉得烦人的一件事是它需要一个 ORD。也许这是使函数更具确定性所必需的,但排序对于许多 folding/reducing 任务并不重要并且会降低性能。我的解决方法是离开 fp-ts,生成一个数组或可迭代的 [key,value] 对,然后对其进行简单的 reduce 以找到年龄最大的人。
import { map as MAP, ord as ORD } from "fp-ts"
type Person = string;
type Age = number;
const oldestPerson = (ps:Map<Person, Age>) =>
pipe(ps, MAP.getFoldableWithIndex<Person>(ORD.fromCompare<Person>((a,b)=>0)))......
刚注意到最近未发布的版本支持 reduce。仍然不知道为什么需要 ORD 或如何使用 getFolderableWithIndex 或何时发布新版本。
https://github.com/gcanti/fp-ts/blob/2.11/src/ReadonlyMap.ts
您可以:
import { ordNumber } from 'fp-ts/Ord'
type Name = string
type Age = number
type Persons = Map<Name, Age>
const persons: Persons = new Map()
persons.set('Joao', 13)
persons.set('Roberto', 20)
const sorted = new Map(
[...persons.entries()].sort((a, b) => ordNumber.compare(b[1], a[1]))
)
但是,请务必注意,在此示例中,您通过调用 .set
.
来改变 persons
变量
更纯粹的函数式方法是:
import { ordNumber } from 'fp-ts/Ord'
import { Eq as StringEq } from 'fp-ts/string'
import * as F from 'fp-ts/function'
import * as M from 'fp-ts/Map'
type Name = string
type Age = number
const insertIntoPersons = M.upsertAt(StringEq)
const persons = F.pipe(
new Map<Name, Age>(),
insertIntoPersons('Joao', 13),
insertIntoPersons('Roberto', 20)
)
const sorted = new Map(
[...persons.entries()].sort((a, b) => ordNumber.compare(b[1], a[1]))
)
如果您想留在“fp-ts
内”,您可以执行以下操作:
import { flow, pipe, tuple } from "fp-ts/function"
import * as Mn from "fp-ts/Monoid"
import * as N from "fp-ts/number"
import * as O from "fp-ts/Option"
import * as Ord from "fp-ts/Ord"
import * as RM from "fp-ts/ReadonlyMap"
import * as RT from "fp-ts/ReadonlyTuple"
import * as Sg from "fp-ts/Semigroup"
import * as S from "fp-ts/string"
type Person = string
type Age = number
type PersonAge = readonly [Person, Age]
const ordByAge: Ord.Ord<PersonAge> = pipe(N.Ord, Ord.contramap(RT.snd))
const monoidMaxByAge: Mn.Monoid<O.Option<PersonAge>> = pipe(
ordByAge,
Sg.max,
O.getMonoid
)
const oldestPerson = (ps: ReadonlyMap<Person, Age>) =>
pipe(
ps,
RM.foldMapWithIndex(S.Ord)(monoidMaxByAge)(flow(tuple, O.some)),
O.map(RT.fst)
)
这很冗长,没有解决您对性能的担忧。
One thing I find annoying is that it requires an ORD. Maybe this is required to make the function more deterministic but sorting is not important for many folding/reducing tasks and decreases performance.
你说得对,这是关于决定论的。本机 JS Map
按插入顺序存储条目,因此如果没有 Ord
键实例,此 oldestPerson
函数可能会为两个给定的 Map
产生不同的结果除了广告订单。我会认为这是意外行为。
这是我希望持有的内容:
import * as assert from "assert"
const aliceBob: ReadonlyMap<Person, Age> = new Map([
["Alice", 25],
["Bob", 25],
])
const bobAlice: ReadonlyMap<Person, Age> = new Map([
["Bob", 25],
["Alice", 25],
])
const emptyMap: ReadonlyMap<Person, Age> = new Map([])
assert.deepStrictEqual(oldestPerson(aliceBob), oldestPerson(bobAlice))
assert.deepStrictEqual(oldestPerson(emptyMap), O.none)
我同意 fp-ts/ReadonlyMap
中的很多函数都不是很有效,我认为它们只是本机数据结构的最小“功能性 API”包装器这与不变性和确定性并不能很好地发挥作用。如果性能更受关注,我可能会像您一样使用本机方法。
something called getFoldableWithIndex
but I don't know how to use it or get it to compile with Typescript.
getFoldableWithIndex
将 return 一个 FoldableWithIndex
类型的 class 实例,它在这里对您没有用。这是一个 value ,您可以将其作为参数传递给需要 FoldableWithIndex
实例的函数,我什至不确定是否有一个很好的例子。在 fp-ts/FoldableWithIndex
中有一个 traverse_
in fp-ts/Foldable
which takes a Foldable
instance, but no corresponding traverseWithIndex_
, though that hypothetical function would take a FoldableWithIndex
instance. There are some functions,但那些是关于 将两个 FoldableWithIndex
实例组合成另一个实例(这在这里没有用)。
您可以直接使用 FoldableWithIndex
:类型 class 实例是一个带有 uncurried foldMapWithIndex
方法的对象,所以如果您想使原始片段 更加冗长 ,您可以这样做:
const oldestPerson = (ps: ReadonlyMap<Person, Age>) =>
pipe(
RM.getFoldableWithIndex(S.Ord).foldMapWithIndex(monoidMaxByAge)(
ps,
flow(tuple, O.some)
),
O.map(RT.fst)
)
但这些实例并不是真的要直接在 pipe
中使用,这就是为什么我会使用顶级 RM.foldMapWithIndex
。有关 classes in fp-ts
的工作原理的更多信息 here.
我希望能够 fold/reduce 一个 Map,就像我可以使用 Array 和 Set 一样。我看到的最接近的是一个叫做 getFoldableWithIndex
的东西,但我不知道如何使用它或让它与 Typescript 一起编译。我觉得烦人的一件事是它需要一个 ORD。也许这是使函数更具确定性所必需的,但排序对于许多 folding/reducing 任务并不重要并且会降低性能。我的解决方法是离开 fp-ts,生成一个数组或可迭代的 [key,value] 对,然后对其进行简单的 reduce 以找到年龄最大的人。
import { map as MAP, ord as ORD } from "fp-ts"
type Person = string;
type Age = number;
const oldestPerson = (ps:Map<Person, Age>) =>
pipe(ps, MAP.getFoldableWithIndex<Person>(ORD.fromCompare<Person>((a,b)=>0)))......
刚注意到最近未发布的版本支持 reduce。仍然不知道为什么需要 ORD 或如何使用 getFolderableWithIndex 或何时发布新版本。
https://github.com/gcanti/fp-ts/blob/2.11/src/ReadonlyMap.ts
您可以:
import { ordNumber } from 'fp-ts/Ord'
type Name = string
type Age = number
type Persons = Map<Name, Age>
const persons: Persons = new Map()
persons.set('Joao', 13)
persons.set('Roberto', 20)
const sorted = new Map(
[...persons.entries()].sort((a, b) => ordNumber.compare(b[1], a[1]))
)
但是,请务必注意,在此示例中,您通过调用 .set
.
persons
变量
更纯粹的函数式方法是:
import { ordNumber } from 'fp-ts/Ord'
import { Eq as StringEq } from 'fp-ts/string'
import * as F from 'fp-ts/function'
import * as M from 'fp-ts/Map'
type Name = string
type Age = number
const insertIntoPersons = M.upsertAt(StringEq)
const persons = F.pipe(
new Map<Name, Age>(),
insertIntoPersons('Joao', 13),
insertIntoPersons('Roberto', 20)
)
const sorted = new Map(
[...persons.entries()].sort((a, b) => ordNumber.compare(b[1], a[1]))
)
如果您想留在“fp-ts
内”,您可以执行以下操作:
import { flow, pipe, tuple } from "fp-ts/function"
import * as Mn from "fp-ts/Monoid"
import * as N from "fp-ts/number"
import * as O from "fp-ts/Option"
import * as Ord from "fp-ts/Ord"
import * as RM from "fp-ts/ReadonlyMap"
import * as RT from "fp-ts/ReadonlyTuple"
import * as Sg from "fp-ts/Semigroup"
import * as S from "fp-ts/string"
type Person = string
type Age = number
type PersonAge = readonly [Person, Age]
const ordByAge: Ord.Ord<PersonAge> = pipe(N.Ord, Ord.contramap(RT.snd))
const monoidMaxByAge: Mn.Monoid<O.Option<PersonAge>> = pipe(
ordByAge,
Sg.max,
O.getMonoid
)
const oldestPerson = (ps: ReadonlyMap<Person, Age>) =>
pipe(
ps,
RM.foldMapWithIndex(S.Ord)(monoidMaxByAge)(flow(tuple, O.some)),
O.map(RT.fst)
)
这很冗长,没有解决您对性能的担忧。
One thing I find annoying is that it requires an ORD. Maybe this is required to make the function more deterministic but sorting is not important for many folding/reducing tasks and decreases performance.
你说得对,这是关于决定论的。本机 JS Map
按插入顺序存储条目,因此如果没有 Ord
键实例,此 oldestPerson
函数可能会为两个给定的 Map
产生不同的结果除了广告订单。我会认为这是意外行为。
这是我希望持有的内容:
import * as assert from "assert"
const aliceBob: ReadonlyMap<Person, Age> = new Map([
["Alice", 25],
["Bob", 25],
])
const bobAlice: ReadonlyMap<Person, Age> = new Map([
["Bob", 25],
["Alice", 25],
])
const emptyMap: ReadonlyMap<Person, Age> = new Map([])
assert.deepStrictEqual(oldestPerson(aliceBob), oldestPerson(bobAlice))
assert.deepStrictEqual(oldestPerson(emptyMap), O.none)
我同意 fp-ts/ReadonlyMap
中的很多函数都不是很有效,我认为它们只是本机数据结构的最小“功能性 API”包装器这与不变性和确定性并不能很好地发挥作用。如果性能更受关注,我可能会像您一样使用本机方法。
something called
getFoldableWithIndex
but I don't know how to use it or get it to compile with Typescript.
getFoldableWithIndex
将 return 一个 FoldableWithIndex
类型的 class 实例,它在这里对您没有用。这是一个 value ,您可以将其作为参数传递给需要 FoldableWithIndex
实例的函数,我什至不确定是否有一个很好的例子。在 fp-ts/FoldableWithIndex
中有一个 traverse_
in fp-ts/Foldable
which takes a Foldable
instance, but no corresponding traverseWithIndex_
, though that hypothetical function would take a FoldableWithIndex
instance. There are some functions,但那些是关于 将两个 FoldableWithIndex
实例组合成另一个实例(这在这里没有用)。
您可以直接使用 FoldableWithIndex
:类型 class 实例是一个带有 uncurried foldMapWithIndex
方法的对象,所以如果您想使原始片段 更加冗长 ,您可以这样做:
const oldestPerson = (ps: ReadonlyMap<Person, Age>) =>
pipe(
RM.getFoldableWithIndex(S.Ord).foldMapWithIndex(monoidMaxByAge)(
ps,
flow(tuple, O.some)
),
O.map(RT.fst)
)
但这些实例并不是真的要直接在 pipe
中使用,这就是为什么我会使用顶级 RM.foldMapWithIndex
。有关 classes in fp-ts
的工作原理的更多信息 here.