diff --git a/__tests__/Map.ts b/__tests__/Map.ts index 66d991676f..f6436f9cbc 100644 --- a/__tests__/Map.ts +++ b/__tests__/Map.ts @@ -473,4 +473,80 @@ describe('Map', () => { Map([[a, Map([[b, Map([[c, 10], [d, 2], [e, 20], [f, 30], [g, 40]])]])]]) ); }); + + it('supports Symbol keys from object literal', () => { + const a = Symbol('A'); + const b = Symbol('B'); + const c = Symbol('C'); + const m = Map.fromOwnEntries({ [a]: 'A', [b]: 'B', [c]: 'C' }); + expect(m.size).toBe(3); + expect(m.get(a)).toBe('A'); + expect(m.get(b)).toBe('B'); + expect(m.get(c)).toBe('C'); + }); + + it('Symbol keys from object literal are unique', () => { + const a = Symbol('FooBar'); + const b = Symbol('FooBar'); + const m = Map.fromOwnEntries({ [a]: 'FizBuz', [b]: 'FooBar' }); + expect(m.size).toBe(2); + expect(m.get(a)).toBe('FizBuz'); + expect(m.get(b)).toBe('FooBar'); + }); + + it('creates nested Map from object with nested Symbol keys', () => { + const a = Symbol('a'); + const b = Symbol('b'); + const c = Symbol('c'); + const e = Symbol('e'); + const f = Symbol('f'); + const g = Symbol('g'); + const map = Map.fromOwnEntries({ + [a]: { [b]: { [c]: 10, [e]: 20 }, [f]: 30 }, + [g]: 40, + }); + + expect( + map + .get(a) + .get(b) + .get(c) + ).toEqual(10); + expect( + map + .get(a) + .get(b) + .get(e) + ).toEqual(20); + expect(map.get(a).get(f)).toEqual(30); + expect(map.get(g)).toEqual(40); + }); + + it('mergeDeep Map from object literal', () => { + const a = Symbol('a'); + const b = Symbol('b'); + const c = Symbol('c'); + const d = Symbol('d'); + const e = Symbol('e'); + const f = Symbol('f'); + const g = Symbol('g'); + const m1 = Map.fromOwnEntries({ [a]: { [b]: { [c]: 1, [d]: 2 } } }); + const m2 = Map.fromOwnEntries({ + [a]: { [b]: { [c]: 10, [e]: 20 }, [f]: 30 }, + [g]: 40, + }); + const expected = Map.fromOwnEntries({ + [a]: { [b]: { [c]: 10, [d]: 2, [e]: 20 }, [f]: 30 }, + [g]: 40, + }); + const actual = m1.mergeDeep(m2); + expect(actual).toEqual(expected); + + const actualJs = actual.toJS(); + expect(actualJs[a][b][c]).toEqual(10); + expect(actualJs[a][b][d]).toEqual(2); + expect(actualJs[a][b][e]).toEqual(20); + expect(actualJs[a][f]).toEqual(30); + expect(actualJs[g]).toEqual(40); + }); }); diff --git a/src/Map.js b/src/Map.js index d9b22e7945..e7327895f6 100644 --- a/src/Map.js +++ b/src/Map.js @@ -66,6 +66,17 @@ export class Map extends KeyedCollection { }); } + static fromOwnEntries(source) { + return Map( + Reflect.ownKeys(source).map(key => [ + key, + typeof source[key] === 'object' + ? Map.fromOwnEntries(source[key]) + : source[key], + ]) + ); + } + toString() { return this.__toString('Map {', '}'); } diff --git a/type-definitions/Immutable.d.ts b/type-definitions/Immutable.d.ts index a330bf4336..64c0c8d2fa 100644 --- a/type-definitions/Immutable.d.ts +++ b/type-definitions/Immutable.d.ts @@ -717,6 +717,48 @@ declare module Immutable { * @deprecated Use Map([ [ 'k', 'v' ] ]) or Map({ k: 'v' }) */ function of(...keyValues: Array): Map; + + /** + * Creates a new Map from an object. + * + * + * ```js + * const { Map } = require('immutable') + * Map.fromOwnEntries({ + * foo: 'bar', + * fiz: 'buz' + * }) + * // Map { "foo": "bar", "fiz": "buz" } + * ``` + * + * Nested objects are recursively converted into instances of Map. + * + * + * ```js + * const { Map } = require('immutable') + * Map.fromOwnEntries({ + * foo: 'bar', + * fiz: { + * buz: 'qux' + * } + * }) + * // Map { "foo": "bar", "fiz": Map { "buz": "qux" } } + * ``` + * + * Symbol keys are supported. + * + * + * ```js + * const { Map } = require('immutable') + * const fiz = Symbol('fiz'); + * Map.fromOwnEntries({ + * foo: 'bar', + * [fiz]: 'buz' + * }) + * // Map { "foo": "bar", "Symbol(fiz)": "buz" } + * ``` + */ + function fromOwnEntries(obj: any): Map; } /** diff --git a/type-definitions/immutable.js.flow b/type-definitions/immutable.js.flow index 9904310f83..d24a8441e1 100644 --- a/type-definitions/immutable.js.flow +++ b/type-definitions/immutable.js.flow @@ -868,6 +868,7 @@ declare class Map extends KeyedCollection mixins UpdatableInCollect size: number; + fromOwnEntries(obj: mixed): Map; set(key: K_, value: V_): Map; delete(key: K): this; remove(key: K): this;