对象是否有类似`.map`的函数?用相同的键创建一个新对象

时间:2019-11-20 01:17:10

标签: javascript functional-programming functor

对于数组,我们可以使用.map方法来创建具有相同数组结构(即,元素数量相同)的新地图。

例如

const array = [2, 4, 6]; 
const newArray = array.map(v => v *2); //[4, 8, 12]; 

我相信在函数式编程中,这会使数组对象称为functor

对于对象,我想做类似的事情-我想创建一个具有与原始对象相同的结构(相同的键)以及相同的功能等的新对象。

例如

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); 

我想知道-当前是否存在一个对象方法,该方法可以让我执行我丢失的操作?还是提议增加一个?

6 个答案:

答案 0 :(得分:3)

没有对象的本机映射,但是可以轻松地创建对象。我认为条目是最简单的。

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)
)

答案 1 :(得分:3)

用于对象的map函数很容易定义。

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函数不同,因为映射函数无法访问或更改密钥。因此,这是Functor类型的对象的正确实现。


作为奖励,让我们为对象实现一些其他有用的类型类。首先,是Representable类型的类。

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函数可用于定义各种有用的类型类,例如MonadComonad。例如,让我们实现Distributive类型类。我将其他类型类的实现留给读者练习。

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需要一个具有map方法的对象,这就是为什么我们在map上定义了Object.prototype方法的原因。但是,我们可以使用Yoneda引理来解决此限制。

答案 2 :(得分:0)

一种方法:

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))

答案 3 :(得分:0)

使用.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)

答案 4 :(得分:0)

如果您想在代码中尝试一些功能性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>

答案 5 :(得分:0)

您可以使用函数lodash的mapValues

const array = [2,4,6];

_。mapValues(array,v => v * 2)