Skip to content

Commit

Permalink
Add fromMap and toMap to URLSearchParams
Browse files Browse the repository at this point in the history
  • Loading branch information
samhh committed Oct 25, 2023
1 parent 67e47b0 commit 14ca3e6
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This project adheres to semantic versioning.
## 0.18.0 (_Unreleased_)

- Add `until` to `Monad`, `IO`, and `Task`.
- Add `singleton`, `appendAt`, `upsertAt`, `deleteAt`, `lookup`, `lookupFirst`, `toLeadingString`, `keys`, `values`, `size` and `concatBy`, and `Eq`, `Semigroup` and `Monoid` instances to `URLSearchParams`.
- Add `singleton`, `appendAt`, `upsertAt`, `deleteAt`, `lookup`, `lookupFirst`, `toLeadingString`, `keys`, `values`, `size`, `concatBy`, `fromMap` and `toMap`, and `Eq`, `Semigroup` and `Monoid` instances to `URLSearchParams`.
- Add `integerFromString` to `Number`.
- Fix `fromString` in `Number` not parsing floats.
- Deprecate `getParam`, `getAllForParam`, and `setParam` in `URLSearchParams`.
Expand Down
63 changes: 63 additions & 0 deletions docs/modules/URLSearchParams.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Added in v0.2.0
- [concatBy](#concatby)
- [deleteAt](#deleteat)
- [empty](#empty)
- [fromMap](#frommap)
- [fromRecord](#fromrecord)
- [fromString](#fromstring)
- [fromTuples](#fromtuples)
Expand All @@ -36,6 +37,7 @@ Added in v0.2.0
- [singleton](#singleton)
- [size](#size)
- [toLeadingString](#toleadingstring)
- [toMap](#tomap)
- [toRecord](#torecord)
- [toString](#tostring)
- [toTuples](#totuples)
Expand Down Expand Up @@ -275,6 +277,36 @@ assert.deepStrictEqual(empty, new URLSearchParams())

Added in v0.2.0

## fromMap

Convert a `Map` to a `URLSearchParams`.

**Signature**

```ts
export declare const fromMap: (x: Map<string, Array<string>>) => URLSearchParams
```
```hs
fromMap :: Map string (Array string) -> URLSearchParams
```

**Example**

```ts
import { fromMap, fromString } from 'fp-ts-std/URLSearchParams'

const m: Map<string, Array<string>> = new Map([
['a', ['b', 'c']],
['d', ['e', 'f']],
])
const s = 'a=b&a=c&d=e&d=f'

assert.deepStrictEqual(fromMap(m), fromString(s))
```

Added in v0.18.0

## fromRecord

Parse a `URLSearchParams` from a record.
Expand Down Expand Up @@ -573,6 +605,37 @@ assert.strictEqual(toLeadingString(new URLSearchParams('a=b')), '?a=b')

Added in v0.18.0

## toMap

Convert a `URLSearchParams` to a `Map`, grouping values by keys.

**Signature**

```ts
export declare const toMap: (x: URLSearchParams) => Map<string, NonEmptyArray<string>>
```
```hs
toMap :: URLSearchParams -> Map string (NonEmptyArray string)
```

**Example**

```ts
import { toMap, fromString } from 'fp-ts-std/URLSearchParams'

// TODO
const u = fromString('a=1&b=3&a=2&b=4')
const m = new Map([
['a', ['1', '2']],
['b', ['3', '4']],
])

assert.deepStrictEqual(toMap(u), m)
```

Added in v0.18.0

## toRecord

Convert a `URLSearchParams` to a record, grouping values by keys.
Expand Down
55 changes: 55 additions & 0 deletions src/URLSearchParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { Option } from "fp-ts/Option"
import * as O from "fp-ts/Option"
import * as R from "fp-ts/Record"
import * as M from "fp-ts/Map"
import { constant, flow, pipe } from "fp-ts/function"
import { Refinement } from "fp-ts/Refinement"
import { invoke, isInstanceOf, uncurry2, when } from "./Function"
Expand Down Expand Up @@ -593,3 +594,57 @@ export const Monoid: Monoid<URLSearchParams> = {
...Semigroup,
empty,
}

/**
* Convert a `Map` to a `URLSearchParams`.
*
* @example
* import { fromMap, fromString } from 'fp-ts-std/URLSearchParams'
*
* const m: Map<string, Array<string>> = new Map([ ['a', ['b', 'c']], ['d', ['e', 'f']] ])
* const s = 'a=b&a=c&d=e&d=f'
*
* assert.deepStrictEqual(fromMap(m), fromString(s))
*
* @category 3 Functions
* @since 0.18.0
*/
export const fromMap: (x: Map<string, Array<string>>) => URLSearchParams = flow(
M.foldMapWithIndex(Str.Ord)(A.getMonoid<[string, string]>())((k, vs) =>
pipe(vs, A.map(withFst(k))),
),
fromTuples,
)

/**
* Convert a `URLSearchParams` to a `Map`, grouping values by keys.
*
* @example
* import { toMap, fromString } from 'fp-ts-std/URLSearchParams'
*
* // TODO
* const u = fromString("a=1&b=3&a=2&b=4")
* const m = new Map([ ['a', ['1', '2']], ['b', ['3', '4']] ])
*
* assert.deepStrictEqual(toMap(u), m)
*
* @category 3 Functions
* @since 0.18.0
*/
// Defined like this as there's currently no `M.fromFoldableMap`.
export const toMap = (
x: URLSearchParams,
/* eslint-disable */
): Map<string, NonEmptyArray<string>> => {
const m = new Map<string, NonEmptyArray<string>>()

for (const [k, v] of x.entries()) {
const prev = M.lookup(Str.Eq)(k)(m)

if (O.isSome(prev)) m.set(k, A.append(v)(prev.value))
else m.set(k, NEA.of(v))
}

return m
}
/* eslint-enable */
42 changes: 42 additions & 0 deletions test/URLSearchParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ import {
values,
size,
concatBy,
fromMap,
toMap,
} from "../src/URLSearchParams"
import fc from "fast-check"
import * as R from "fp-ts/Record"
import * as M from "fp-ts/Map"
import * as O from "fp-ts/Option"
import * as A from "fp-ts/Array"
import * as T from "fp-ts/Tuple"
import * as Str from "fp-ts/string"
import { constant, flip, pipe } from "fp-ts/function"
import * as laws from "fp-ts-laws"
import { not } from "fp-ts/Predicate"
Expand Down Expand Up @@ -480,4 +484,42 @@ describe("URLSearchParams", () => {
laws.monoid(Monoid, Eq, arb)
})
})

describe("fromMap", () => {
const f = fromMap

const mapArb: fc.Arbitrary<Map<string, Array<string>>> = fc
.array(fc.tuple(fc.string(), fc.array(fc.string())))
.map(M.fromFoldable(Str.Eq, A.getSemigroup<string>(), A.Foldable))

it("is infallible", () => {
fc.assert(fc.property(mapArb, xs => isURLSearchParams(f(xs))))
})

it("takes all key/value pairs", () => {
const xs: Array<[string, Array<string>]> = [
["a", ["1", "2"]],
["b", ["3"]],
]

expect(f(new Map(xs))).toEqual(fromString("a=1&a=2&b=3"))
})
})

describe("toMap", () => {
const f = toMap

it("is infallible", () => {
fc.assert(fc.property(arb, xs => f(xs) instanceof Map))
})

it("takes all key/value pairs", () => {
const xs: Array<[string, Array<string>]> = [
["a", ["1", "3"]],
["b", ["2"]],
]

expect(f(fromString("a=1&b=2&a=3"))).toEqual(new Map(xs))
})
})
})

0 comments on commit 14ca3e6

Please sign in to comment.