├── .gitattributes ├── .gitignore ├── .npmrc ├── DETAILS.md ├── LICENSE ├── README.md ├── index.html ├── package.json ├── polyfill.js └── spec.html /.gitattributes: -------------------------------------------------------------------------------- 1 | index.html -diff merge=ours 2 | spec.js -diff merge=ours 3 | spec.css -diff merge=ours 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Only apps should have lockfiles 40 | yarn.lock 41 | package-lock.json 42 | npm-shrinkwrap.json 43 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /DETAILS.md: -------------------------------------------------------------------------------- 1 | # Details 2 | 3 | There are number of decisions which could be made differently. This document attempts to catalog them along with the rationales for the choices currently made. 4 | 5 | 6 | ### Naming 7 | 8 | The method in this proposal is named `fromEntries`. There already exists `Array.from`, and there is [a proposal](https://github.com/tc39/proposal-setmap-offrom) to add `{Set, Map, WeakSet, WeakMap}.from`; it could be argued that the method in this proposal should therefore be named `.from` for consistency. 9 | 10 | However, this is less of a "canonical" way of constructing objects than those methods are or would be, especially since this method does not provide for defining non-data properties or setting a prototype. As such, `fromEntries` was chosen instead, to match `Object.entries`. 11 | 12 | Issue: [#7](https://github.com/bakkot/object-from-entries/issues/7). 13 | 14 | 15 | ### Placement 16 | 17 | This proposal adds a static method to `Object`. In principle the same functionality could be provided by a similar function elsewhere, e.g. `Array.prototype.toObject`. 18 | 19 | This proposal instead chooses consistency with `Object.entries` and `Array.from`. 20 | 21 | 22 | ### `ToPropertyKey` 23 | 24 | This proposal coerces the first entry in the list of pairs to a property key using the [ToPropertyKey](https://tc39.github.io/ecma262/#sec-topropertykey) abstract operation. 25 | 26 | This is consistent with other methds of creating or assigning to properties on objects. 27 | 28 | 29 | ### Symbol keys 30 | 31 | `Object.entries` does not report Symbol-keyed entries, but this proposal allows their creation (for example, `let prop = Symbol(); let obj = Object.fromEntries([[prop, 0]]); console.log(obj[prop]);` will report `0`). This means that `Object.entries(Object.fromEntries(arr))` may yield an array with fewer elements than `arr`. 32 | 33 | However, `fromEntries` is strictly more useful this way, and any other behavior would probably be more surprising. 34 | 35 | Issue: [#5](https://github.com/bakkot/object-from-entries/issues/5). 36 | 37 | 38 | ### List of records vs list of iterables 39 | 40 | `fromEntries` expects the key-value pairs yielded by its argument to be objects with keys `"0"` and `"1"` which give the key and value respectively, rather than being iterables whose first and second items give the key and value respectively. 41 | 42 | This was chosen for consistency with the [`Map` constructor](https://tc39.github.io/ecma262/#sec-map-iterable), which takes a list of entires in precisely the same way (except that it does not coerce keys with `ToPropertyKey`). 43 | 44 | In the common case of arrays as this behavior is equivalent unless someone has overridden `Array.prototype[@@iterator]`. 45 | 46 | 47 | ### Requirement that records be objects 48 | 49 | This proposal throws if the iterator yields a value which is not an object (that is, an `x` such that `Object(x) !== x`). This means that, for example, yielding a length-2 `string` will not work, even though the later steps which access the `"0"` and `"1"` properties would succeed and then use the first and second character of the string as the key and value respectively of a property on the resulting object. 50 | 51 | This is consistent with the `Map` constructor. 52 | 53 | 54 | ### Iterable or array-like 55 | 56 | This proposal requires its first argument to be an iterable, not an "array-like" (an object with a number-valued `length` property and possibly number-keyed properties). This means that passing, for example, an `arguments` object will cause this method to throw (assuming no one has defined `Object.prototype[@@iterator]`). This is inconsistent with `Array.from`, which accepts "array-likes". 57 | 58 | However, the behavior of `Array.from` is very specifically [intended for the conversion of array-likes](https://github.com/tc39/proposal-setmap-offrom/issues/3#issue-175135115), and does not need to be copied elsewhere. 59 | 60 | Issue: [#9](https://github.com/bakkot/object-from-entries/issues/9). 61 | 62 | 63 | ### Non-optionality of argument 64 | 65 | This proposal's first argument is not optional, meaning a `TypeError` is thrown if it's not provided. 66 | 67 | This is unlike the `Map` constructor but like `Array.from` and [most other uses of the iteration protocol](https://github.com/tc39/ecma262/pull/1069#issuecomment-360043550). The `Map` constructor is also used to construct new empty `Map`s, whereas this is not the preferred way of constructing new empty objects. 68 | 69 | Issue: [#6](https://github.com/bakkot/object-from-entries/issues/6). 70 | 71 | 72 | ### Evaluation order 73 | 74 | This proposal chooses to get the value from the entry record before coercing the key with `ToPropertyKey`. This matters because both of these steps are observable. 75 | 76 | This is somewhat arbitrary, but is consistent with regular assignment: `({}[{ toString(){ console.log(2); return ''; } }] = console.log(1))` prints `1, 2`. 77 | 78 | 79 | ### Prototype of new object 80 | 81 | This proposal does not allow specifying the prototype of the resulting object. 82 | 83 | However, `Object.assign(Object.create(proto), Object.fromEntries(entries))` can be used for the same effect (modulo [[Set]] vs [[DefineOwnProperty]] differences). 84 | 85 | Issue: [#13](https://github.com/bakkot/object-from-entries/issues/13). 86 | 87 | 88 | ### Non-data properties 89 | 90 | This proposal does not allow the creation of non-data properties, for example from a list of key-property descriptor pairs. 91 | 92 | However, given such a list, `Object.defineProperties({}, Object.fromEntries(list))` can be used for the same effect. 93 | 94 | 95 | ### Installation of properties on existing object 96 | 97 | This proposal does not allow installing properties on an existing object. 98 | 99 | However, `Object.assign(obj, Object.fromEntries(entries))` can be used for the same effect (modulo [[Set]] vs [[DefineOwnProperty]] differences). 100 | 101 | 102 | ### [[Set]] vs [[DefineOwnProperty]] 103 | 104 | This proposal uses `defineOwnProperty` semantics to install properties on the newly-created object. 105 | 106 | This is consistent with `Object.create` and `Array.from`, and avoids triggering setters on `Object.prototype`. 107 | 108 | Issue: [#2](https://github.com/bakkot/object-from-entries/issues/2). 109 | 110 | 111 | ### Duplicate keys 112 | 113 | This proposal does not explicitly handle duplicate keys, with the result that later occurrences of a given key will take precedence over earlier ones. 114 | 115 | This is consistent with the `Map` constructor. 116 | 117 | 118 | ### Mapping function 119 | 120 | `Array.from` allows specifying a mapping function; this proposal does not. 121 | 122 | The mapping function on `Array.from` is more necessary because of `Array.from`'s use for converting arbitrary iterables to arrays. 123 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ECMA TC39 and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Object.fromEntries 2 | 3 | This repository is now archived, since the proposal is stage 4. 4 | 5 | A proposal for a new static method `Object.fromEntries` in ECMAScript for transforming a list of key-value pairs into an object. 6 | 7 | This proposal was originally written by [Darien Maillet Valentine](https://github.com/bathos) and is being championed by [Jordan Harband](https://github.com/ljharb) and [Kevin Gibbons](https://github.com/bakkot). 8 | 9 | 10 | 11 | - [Proposal](#proposal) 12 | - [Rationale](#rationale) 13 | - [When is this useful?](#when-is-this-useful) 14 | - [Motivating examples](#motivating-examples) 15 | - [Object-to-object property transformations](#object-to-object-property-transformations) 16 | - [Object from existing collection](#object-from-existing-collection) 17 | - [Prior art](#prior-art) 18 | - [Lodash](#lodash) 19 | - [Python](#python) 20 | 21 | 22 | 23 | ## Proposal 24 | 25 | `Object.fromEntries` is proposed to perform the reverse of `Object.entries`: it accepts an iterable of key-value pairs and returns a new object whose own keys and corresponding values are given by those pairs. 26 | 27 | ```js 28 | obj = Object.fromEntries([['a', 0], ['b', 1]]); // { a: 0, b: 1 } 29 | ``` 30 | 31 | See [DETAILS.md](https://github.com/bakkot/object-from-entries/blob/master/DETAILS.md) for details. 32 | 33 | 34 | ## Rationale 35 | 36 | It's common to transform data held in various structures (arrays, maps, etc) from one form to another. When the data structures in question are both iterable this is typically simple: 37 | 38 | ```js 39 | map = new Map().set('foo', true).set('bar', false); 40 | arr = Array.from(map); 41 | set = new Set(map.values()); 42 | ``` 43 | 44 | The iterable entries of a `Map` take the form of key-value pairs. This dovetails nicely with the pairs returned by `Object.entries`, such that you can convert objects to `Map`s fairly expressively: 45 | 46 | ```js 47 | obj = { foo: true, bar: false }; 48 | map = new Map(Object.entries(obj)); 49 | ``` 50 | 51 | However there is no inverse of `Object.entries` for constructing objects from key-value pairs, so to do so one typically must write a helper or inline reducer: 52 | 53 | ```js 54 | obj = Array.from(map).reduce((acc, [ key, val ]) => Object.assign(acc, { [key]: val }), {}); 55 | ``` 56 | 57 | This can be written many different ways, and potentially adds noise because it's not likely to be obviously related to the outward purpose of the function doing it. 58 | 59 | ### When is this useful? 60 | 61 | `Object.fromEntries` doesn’t imply a preference for ordinary objects over `Map`. 62 | 63 | If you have a collection with an arbitrary set of keys, even if they’re strings, 64 | and especially if you intend to add/remove members over time, `Map` data is 65 | likely a more appropriate model than object properties. Properties are 66 | well-suited to describing interfaces or fixed-shape models, but poorly suited 67 | for modeling arbitrary hashes, and `Map` aims to serve that case better. 68 | 69 | We don’t always get to choose the model. This is one of the things `fromEntries` 70 | is meant to help with. Recognizing that some data is an arbitrary collection, 71 | one might prefer to model it using `Map`. Later that data may need to be passed 72 | to an API whose contract expects it to be modeled as an ordinary object though 73 | (think query params, headers, etc): `externalAPI(Object.fromEntries(myMap))`. 74 | 75 | Data that comes from or must be serializable to JSON often uses properties to 76 | model arbitrary collections. Metaprogramming that reflects on entries is another 77 | scenario where we may manipulate or filter entries and then wish to convert them 78 | back into an object — for example, when processing objects suitable for passing 79 | to `Object.defineProperties`. For one more example, while not everybody agrees 80 | on whether it’s a good idea, contracts involving arbitrary-key objects may also 81 | be chosen deliberately if an author feels they improve API ergonomics. 82 | 83 | ## Motivating examples 84 | 85 | ### Object-to-object property transformations 86 | 87 | This allows the easy use of familiar array manipulation methods to transform objects: 88 | 89 | ```js 90 | obj = { abc: 1, def: 2, ghij: 3 }; 91 | res = Object.fromEntries( 92 | Object.entries(obj) 93 | .filter(([ key, val ]) => key.length === 3) 94 | .map(([ key, val ]) => [ key, val * 2 ]) 95 | ); 96 | 97 | // res is { 'abc': 2, 'def': 4 } 98 | ``` 99 | 100 | ### Object from existing collection 101 | 102 | A string-keyed `Map` can be converted to an object, just as an object can already be converted to a `Map`: 103 | 104 | ```js 105 | map = new Map([ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]); 106 | obj = Object.fromEntries(map); 107 | 108 | // compare existing functionality: new Map(Object.entries(obj)) 109 | ``` 110 | 111 | This transformation may be simple for other `Map`-like objects as well: 112 | 113 | ```js 114 | query = Object.fromEntries(new URLSearchParams('foo=bar&baz=qux')); 115 | ``` 116 | 117 | For other collections, intermediate transformations can put the collection in the required form: 118 | 119 | ```js 120 | arr = [ { name: 'Alice', age: 40 }, { name: 'Bob', age: 36 } ]; 121 | obj = Object.fromEntries(arr.map(({ name, age }) => [ name, age ])); 122 | ``` 123 | 124 | ## Prior art 125 | 126 | ### Lodash 127 | 128 | Underscore and Lodash provide a [`_.fromPairs`](https://lodash.com/docs/4.17.4#fromPairs) function which constructs an object from a list of key-value pairs. 129 | 130 | ### Python 131 | 132 | In Python, a `dict` can be initialized with [an array of key-value tuples](https://docs.python.org/3/library/stdtypes.html#dict): 133 | 134 | ```python 135 | dict([('two', 2), ('one', 1), ('three', 3)]) 136 | ``` 137 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 |Object.fromEntries
takes a list of key-value pairs, such as that produced by Object.entries
, and returns a new object whose properties are given by those entries. Its argument iterable is expected to be an object that implements an @@iterator method, that returns an iterator object, that produces a two element array-like object, whose first element is a value that will be used as a property key, and whose second element is the value to associate with that property key.
When the fromEntries
method is called with argument iterable, the following steps are taken:
A CreateDataPropertyOnObject function is an anonymous built-in function. When a CreateDataPropertyOnObject function is called with arguments key and value, the following steps are taken:
1829 |© 2019 Darien Maillet Valentine, Jordan Harband, Kevin Gibbons
1835 | 1836 |All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.
1838 | 1839 |Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1840 | 1841 |THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1848 | 1849 |7 | title: Object.fromEntries 8 | stage: 4 9 | contributors: Darien Maillet Valentine, Jordan Harband, Kevin Gibbons 10 |11 | 12 |
`Object.fromEntries` takes a list of key-value pairs, such as that produced by `Object.entries`, and returns a new object whose properties are given by those entries. Its argument _iterable_ is expected to be an object that implements an @@iterator method, that returns an iterator object, that produces a two element array-like object, whose first element is a value that will be used as a property key, and whose second element is the value to associate with that property key.
15 |When the `fromEntries` method is called with argument _iterable_, the following steps are taken:
20 |A CreateDataPropertyOnObject function is an anonymous built-in function. When a CreateDataPropertyOnObject function is called with arguments _key_ and _value_, the following steps are taken:
36 |