[英]Is there a `.map` like function for objects? To create a new object with the same keys
對於數組,我們可以使用.map
方法創建具有相同數組結構(即相同數量的元素)的新 map。
例如。
const array = [2, 4, 6];
const newArray = array.map(v => v *2); //[4, 8, 12];
我相信在函數式編程中,這使得數組 object 成為所謂的functor 。
對於對象,我想做類似的事情——我想創建一個新的 object,它具有與原始 object 相同的結構(相同的鍵)以及相同的功能等。
例如。
const obj = {
foo: 2,
bar: 4,
biz: 6
};
const newObj = obj.map(v => v *2); // {foo: 4, bar: 8, biz: 12}
我目前這樣做的方式是使用Object.entries
和.reduce
:
const obj = { foo: 2, bar: 4, biz: 6 }; const newObj = Object.entries(obj).reduce((acc,cur) => { return {...acc, [cur[0]]: cur[1] * 2 } }, {}); console.log(newObj);
我想知道 - 目前是否有一種 object 方法可以讓我做到這一點,我錯過了? 還是建議增加一個?
對象沒有原生的 map,但可以輕松制作。 我認為條目是最簡單的。
const obj = { foo: 2, bar: 4, biz: 6 } const objMap = (obj, fn) => Object.fromEntries( Object.entries(obj).map( ([k, v], i) => [k, fn(v, k, i)] ) ) console.log( objMap(obj, v => v * 2), objMap(obj, (v, k) => `${k}-${v}`), objMap(obj, (v, _, i) => v * i) )
對象的map
function 很容易定義。
Object.defineProperty(Object.prototype, "map", { value(mapping) { const oldEntries = Object.entries(this); const newEntries = oldEntries.map(([key, val]) => [key, mapping(val)]); return Object.fromEntries(newEntries); } }); const obj = { foo: 2, bar: 4, baz: 6 }; const result = obj.map(x => 2 * x); console.log(result);
請注意,這與 kornieff 的objMap
function 不同,因為映射 function 無法訪問或更改密鑰。 因此,它是對象的Functor
類型 class 的正確實現。
作為獎勵,讓我們為對象實現一些其他有用的類型類。 一、 Representable
型class。
Object.tabulate = table => new Proxy({}, { get: (_, key) => table(key) }); const object = Object.tabulate(key => { if (key === "foo") return 10; if (key === "bar") return 20; return 30; }); console.log(object.foo); // 10 console.log(object.bar); // 20 console.log(object.baz); // 30
tabulate
function 可用於定義各種有用的類型類,例如Monad
和Comonad
。 例如,讓我們實現Distributive
類型 class。 我將把其他類型類的實現留給讀者作為練習。
Object.tabulate = table => new Proxy({}, { get: (_, key) => table(key) }); const distribute = functor => Object.tabulate(key => functor.map(object => object[key])); const result1 = distribute([ { foo: 10, bar: 20 }, { foo: 30, bar: 40 } ]); console.log(result1.foo); // [ 10, 30 ] console.log(result1.bar); // [ 20, 40 ] Object.defineProperty(Object.prototype, "map", { value(mapping) { const oldEntries = Object.entries(this); const newEntries = oldEntries.map(([key, val]) => [key, mapping(val)]); return Object.fromEntries(newEntries); } }); const result2 = distribute({ a: { foo: 10, bar: 20 }, b: { foo: 30, bar: 40 } }); console.log(result2.foo); // { a: 10, b: 30 } console.log(result2.bar); // { a: 20, b: 40 }
請注意, distribute
期望 object 具有map
方法,這就是我們在 Z497031794151AAC3B5443 上定義map
方法的Object.prototype
。 然而,我們可以使用Yoneda
引理來繞過這個限制。
一種方法:
const obj = { foo: 2, bar: 4, biz: 6 }; const x = Object.keys(obj).map(o => { return {[o]: obj[o] * 2} }) console.log(JSON.stringify(x, null, 2))
使用.reduce()
的另一種方法是這樣的,使用解構來更容易閱讀(我認為)
const obj = { foo: 2, bar: 4, biz: 6 }; const newObj = Object.entries(obj).reduce( (acc, [key, value]) => ({...acc, [key]: value * 2}), {} ); console.log(newObj)
如果您想在代碼中嘗試一些功能性 API,那么您可以考慮使用功能性庫(例如 Ramda),
它不僅介紹了對象(地圖)的函子 api,還介紹了其他很酷的東西!
const input = { a: 1, b: 44, c: 77 }; const doubled = R.map(R.multiply(2), input); console.log( `doubling input`, doubled, ); // and a lot more... const odds = R.filter(R.modulo(R.__, 2), input); console.log( `filtering odds only of input`, odds, );
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.