Repository: gcanti/fp-ts
Pull request #1552 (2021-07-31 22:53:02)

Added Record toEntries, fromEntries
* Added `Record.toEntries` and `Record.fromEntries`, to facilitate a common need of folks coming over from 'normal' JS.


Patch:
diff --git a/docs/modules/ReadonlyRecord.ts.md b/docs/modules/ReadonlyRecord.ts.md
index 01f7c355..869e96c7 100644
--- a/docs/modules/ReadonlyRecord.ts.md
+++ b/docs/modules/ReadonlyRecord.ts.md
@@ -85,6 +85,7 @@ Added in v2.5.0
   - [every](#every)
   - [filterWithIndex](#filterwithindex)
   - [foldMapWithIndex](#foldmapwithindex)
+  - [fromEntries](#fromentries)
   - [fromFoldable](#fromfoldable)
   - [fromFoldableMap](#fromfoldablemap)
   - [has](#has)
@@ -101,6 +102,7 @@ Added in v2.5.0
   - [sequence](#sequence)
   - [size](#size)
   - [some](#some)
+  - [toEntries](#toentries)
   - [toReadonlyArray](#toreadonlyarray)
   - [traverse](#traverse)
   - [traverseWithIndex](#traversewithindex)
@@ -1312,6 +1314,33 @@ assert.deepStrictEqual(foldMapWithIndex(Ord)(m)(f)(x), 'a-1 -> b-2 -> c-3')
 
 Added in v2.5.0
 
+## fromEntries
+
+Converts an `Array` of `[key, value]` tuples into a `Record`.
+
+**Signature**
+
+```ts
+export declare const fromEntries: <A>(fa: readonly (readonly [string, A])[]) => Record<string, A>
+```
+
+**Example**
+
+```ts
+import { fromEntries } from 'fp-ts/Record'
+
+assert.deepStrictEqual(
+  fromEntries([
+    ['a', 1],
+    ['b', 2],
+    ['a', 3],
+  ]),
+  { b: 2, a: 3 }
+)
+```
+
+Added in v2.12.0
+
 ## fromFoldable
 
 Create a `ReadonlyRecord` from a foldable collection of key/value pairs, using the
@@ -1803,6 +1832,29 @@ assert.deepStrictEqual(some((n: number) => n >= 0)({ a: -1, b: -2 }), false)
 
 Added in v2.5.0
 
+## toEntries
+
+Alias of [`toReadonlyArray`](#toReadonlyArray).
+
+**Signature**
+
+```ts
+export declare const toEntries: <K extends string, A>(r: Readonly<Record<K, A>>) => readonly (readonly [K, A])[]
+```
+
+**Example**
+
+```ts
+import { toEntries } from 'fp-ts/Record'
+
+assert.deepStrictEqual(toEntries({ a: 1, b: 2 }), [
+  ['a', 1],
+  ['b', 2],
+])
+```
+
+Added in v2.12.0
+
 ## toReadonlyArray
 
 Get a sorted `ReadonlyArray` of the key/value pairs contained in a `ReadonlyRecord`.
diff --git a/docs/modules/Record.ts.md b/docs/modules/Record.ts.md
index c207c7fd..7cdf3cd6 100644
--- a/docs/modules/Record.ts.md
+++ b/docs/modules/Record.ts.md
@@ -70,6 +70,7 @@ Added in v2.0.0
   - [filterMapWithIndex](#filtermapwithindex)
   - [filterWithIndex](#filterwithindex)
   - [foldMapWithIndex](#foldmapwithindex)
+  - [fromEntries](#fromentries)
   - [fromFoldable](#fromfoldable)
   - [fromFoldableMap](#fromfoldablemap)
   - [has](#has)
@@ -90,6 +91,7 @@ Added in v2.0.0
   - [size](#size)
   - [some](#some)
   - [toArray](#toarray)
+  - [toEntries](#toentries)
   - [toUnfoldable](#tounfoldable)
   - [traverse](#traverse)
   - [traverseWithIndex](#traversewithindex)
@@ -1100,6 +1102,33 @@ assert.deepStrictEqual(foldMapWithIndex(Ord)(m)(f)(x), 'a-1 -> b-2 -> c-3')
 
 Added in v2.0.0
 
+## fromEntries
+
+Converts an `Array` of `[key, value]` tuples into a `Record`.
+
+**Signature**
+
+```ts
+export declare const fromEntries: <A>(fa: [string, A][]) => Record<string, A>
+```
+
+**Example**
+
+```ts
+import { fromEntries } from 'fp-ts/Record'
+
+assert.deepStrictEqual(
+  fromEntries([
+    ['a', 1],
+    ['b', 2],
+    ['a', 3],
+  ]),
+  { b: 2, a: 3 }
+)
+```
+
+Added in v2.12.0
+
 ## fromFoldable
 
 Create a `Record` from a foldable collection of key/value pairs, using the
@@ -1687,6 +1716,29 @@ assert.deepStrictEqual(toArray(x), [
 
 Added in v2.0.0
 
+## toEntries
+
+Alias of [`toArray`](#toArray).
+
+**Signature**
+
+```ts
+export declare const toEntries: <K extends string, A>(r: Record<K, A>) => [K, A][]
+```
+
+**Example**
+
+```ts
+import { toEntries } from 'fp-ts/Record'
+
+assert.deepStrictEqual(toEntries({ a: 1, b: 2 }), [
+  ['a', 1],
+  ['b', 2],
+])
+```
+
+Added in v2.12.0
+
 ## toUnfoldable
 
 Unfolds a `Record` into a list of key/value pairs.
diff --git a/src/ReadonlyRecord.ts b/src/ReadonlyRecord.ts
index 9ff9da19..b11a1e9d 100644
--- a/src/ReadonlyRecord.ts
+++ b/src/ReadonlyRecord.ts
@@ -7,6 +7,7 @@
  * @since 2.5.0
  */
 import { Applicative, Applicative1, Applicative2, Applicative2C, Applicative3, Applicative3C } from './Applicative'
+import * as RA from './ReadonlyArray'
 import { Compactable1 } from './Compactable'
 import { Either } from './Either'
 import { Eq, fromEquals } from './Eq'
@@ -25,7 +26,7 @@ import { Option } from './Option'
 import { Ord } from './Ord'
 import { Predicate } from './Predicate'
 import { Refinement } from './Refinement'
-import { Semigroup } from './Semigroup'
+import * as Se from './Semigroup'
 import { Separated, separated } from './Separated'
 import { Show } from './Show'
 import * as S from './string'
@@ -34,6 +35,8 @@ import { TraversableWithIndex1 } from './TraversableWithIndex'
 import { Unfoldable, Unfoldable1 } from './Unfoldable'
 import { PipeableWilt1, PipeableWither1, wiltDefault, Witherable1, witherDefault } from './Witherable'
 
+import Semigroup = Se.Semigroup
+
 // -------------------------------------------------------------------------------------
 // model
 // -------------------------------------------------------------------------------------
@@ -1082,6 +1085,31 @@ export function fromFoldableMap<F, B>(
   }
 }
 
+/**
+ * Alias of [`toReadonlyArray`](#toReadonlyArray).
+ *
+ * @since 2.12.0
+ *
+ * @example
+ * import { toEntries } from 'fp-ts/Record'
+ *
+ * assert.deepStrictEqual(toEntries({ a: 1, b: 2 }), [['a', 1], ['b', 2]])
+ */
+export const toEntries = toReadonlyArray
+
+/**
+ * Converts an `Array` of `[key, value]` tuples into a `Record`.
+ *
+ * @since 2.12.0
+ *
+ * @example
+ * import { fromEntries } from 'fp-ts/Record'
+ *
+ * assert.deepStrictEqual(fromEntries([['a', 1], ['b', 2], ['a', 3]]), { b: 2, a: 3 })
+ */
+export const fromEntries = <A>(fa: ReadonlyArray<readonly [string, A]>): Record<string, A> =>
+  fromFoldable(Se.last<A>(), RA.Foldable)(fa)
+
 /**
  * Test if every value in a `ReadonlyRecord` satisfies the predicate.
  *
diff --git a/src/Record.ts b/src/Record.ts
index 94c3fb59..5ef2fd46 100644
--- a/src/Record.ts
+++ b/src/Record.ts
@@ -5,6 +5,7 @@
  * @since 2.0.0
  */
 import { Applicative, Applicative1, Applicative2, Applicative2C, Applicative3, Applicative3C } from './Applicative'
+import * as A from './Array'
 import { Compactable1 } from './Compactable'
 import { Either } from './Either'
 import { Eq } from './Eq'
@@ -24,7 +25,7 @@ import { Ord } from './Ord'
 import { Predicate } from './Predicate'
 import * as RR from './ReadonlyRecord'
 import { Refinement } from './Refinement'
-import { Semigroup } from './Semigroup'
+import * as Se from './Semigroup'
 import { Separated } from './Separated'
 import { Show } from './Show'
 import * as S from './string'
@@ -33,6 +34,8 @@ import { TraversableWithIndex1 } from './TraversableWithIndex'
 import { Unfoldable, Unfoldable1 } from './Unfoldable'
 import { PipeableWilt1, PipeableWither1, wiltDefault, Witherable1, witherDefault } from './Witherable'
 
+import Semigroup = Se.Semigroup
+
 // -------------------------------------------------------------------------------------
 // model
 // -------------------------------------------------------------------------------------
@@ -760,6 +763,30 @@ export function fromFoldable<F, A>(M: Magma<A>, F: FoldableHKT<F>): (fka: HKT<F,
   return RR.fromFoldable(M, F)
 }
 
+/**
+ * Alias of [`toArray`](#toArray).
+ *
+ * @since 2.12.0
+ *
+ * @example
+ * import { toEntries } from 'fp-ts/Record'
+ *
+ * assert.deepStrictEqual(toEntries({ a: 1, b: 2 }), [['a', 1], ['b', 2]])
+ */
+export const toEntries = toArray
+
+/**
+ * Converts an `Array` of `[key, value]` tuples into a `Record`.
+ *
+ * @since 2.12.0
+ *
+ * @example
+ * import { fromEntries } from 'fp-ts/Record'
+ *
+ * assert.deepStrictEqual(fromEntries([['a', 1], ['b', 2], ['a', 3]]), { b: 2, a: 3 })
+ */
+export const fromEntries = <A>(fa: Array<[string, A]>): Record<string, A> => fromFoldable(Se.last<A>(), A.Foldable)(fa)
+
 /**
  * Create a `Record` from a foldable collection using the specified functions to
  *


Test patch:
diff --git a/test/ReadonlyRecord.ts b/test/ReadonlyRecord.ts
index abbe8cca..44f8a246 100644
--- a/test/ReadonlyRecord.ts
+++ b/test/ReadonlyRecord.ts
@@ -401,6 +401,24 @@ describe('ReadonlyRecord', () => {
     U.deepStrictEqual(_.toUnfoldable(RA.Unfoldable)({ a: 1 }), [['a', 1]])
   })
 
+  it('toEntries', () => {
+    U.deepStrictEqual(_.toEntries({ a: 1, b: 2 }), [
+      ['a', 1],
+      ['b', 2]
+    ])
+  })
+
+  it('fromEntries', () => {
+    U.deepStrictEqual(
+      _.fromEntries([
+        ['a', 1],
+        ['b', 2],
+        ['a', 3]
+      ]),
+      { b: 2, a: 3 }
+    )
+  })
+
   it('traverseWithIndex should sort the keys', () => {
     // tslint:disable-next-line: readonly-array
     const log: Array<string> = []
diff --git a/test/Record.ts b/test/Record.ts
index 10030019..c9afc05a 100644
--- a/test/Record.ts
+++ b/test/Record.ts
@@ -392,6 +392,24 @@ describe('Record', () => {
     U.deepStrictEqual(_.toUnfoldable(A.Unfoldable)({ a: 1 }), [['a', 1]])
   })
 
+  it('toEntries', () => {
+    U.deepStrictEqual(_.toEntries({ a: 1, b: 2 }), [
+      ['a', 1],
+      ['b', 2]
+    ])
+  })
+
+  it('fromEntries', () => {
+    U.deepStrictEqual(
+      _.fromEntries([
+        ['a', 1],
+        ['b', 2],
+        ['a', 3]
+      ]),
+      { b: 2, a: 3 }
+    )
+  })
+
   it('traverseWithIndex should sort the keys', () => {
     // tslint:disable-next-line: readonly-array
     const log: Array<string> = []