オブジェクトのマップ関数(配列の代わり)


1066

私はオブジェクトを持っています:

myObject = { 'a': 1, 'b': 2, 'c': 3 }

Array.prototype.map次のように使用されるのと同様のネイティブメソッドを探しています。

newObject = myObject.map(function (value, label) {
    return value * value;
});

// newObject is now { 'a': 1, 'b': 4, 'c': 9 }

JavaScriptはmapオブジェクトに対してそのような関数を持っていますか?(これはNode.JSに必要なので、ブラウザー間の問題は気にしません。)


1
ほとんどの回答はを使用Object.keysしていますが、これには明確な順序はありません。これは問題になる可能性があるので、Object.getOwnPropertyNames代わりに使用することをお勧めします。
Oriol

14
JSがこの信じられないほど初歩的な仕事を提供しなかったことは私にとって驚くべきことです。
jchook 2017年

3
@オリオールあなたはそれについて確信していますか?MDN Webドキュメントによると、配列要素の順序はObject.keysとの間で一貫していObject.getOwnPropertyNamesます。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…を
Bart

@Bartの順序Object.keysは実装に依存します。stackoverflow.com/a/30919039/1529630を
Oriol

4
@オリオールあなたはとにかくオブジェクトのキーの順序に依存することになっているわけではないので、彼は順序が重要であると指定したことがないので、それはちょっと質問には無関係です。そして、彼にとって順序が重要である場合、彼はとにかくオブジェクトをまったく使用すべきではありません。
BT

回答:


1543

そこにはネイティブではないmapObjectオブジェクトが、どのようにこのことについて:

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

Object.keys(myObject).map(function(key, index) {
  myObject[key] *= 2;
});

console.log(myObject);
// => { 'a': 2, 'b': 4, 'c': 6 }

しかし、以下を使用してオブジェクトを簡単に反復できますfor ... in

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

for (var key in myObject) {
  if (myObject.hasOwnProperty(key)) {
    myObject[key] *= 2;
  }
}

console.log(myObject);
// { 'a': 2, 'b': 4, 'c': 6 }

更新

多くの人が、以前のメソッドは新しいオブジェクトを返さず、オブジェクト自体を操作することに言及しています。そのため、新しいオブジェクトを返し、元のオブジェクトをそのままにする別のソリューションを追加したかったのです。

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

// returns a new object with the values at each key mapped using mapFn(value)
function objectMap(object, mapFn) {
  return Object.keys(object).reduce(function(result, key) {
    result[key] = mapFn(object[key])
    return result
  }, {})
}

var newObject = objectMap(myObject, function(value) {
  return value * 2
})

console.log(newObject);
// => { 'a': 2, 'b': 4, 'c': 6 }

console.log(myObject);
// => { 'a': 1, 'b': 2, 'c': 3 }

Array.prototype.reduce以前の値を現在の値といくらかマージすることにより、配列を単一の値に減らします。チェーンは空のオブジェクトによって初期化されます{}。すべての反復で、新しいキーがmyObject値の2倍のキーで追加されます。

更新

ES6の新機能により、よりエレガントな表現方法がありobjectMapます。

const objectMap = (obj, fn) =>
  Object.fromEntries(
    Object.entries(obj).map(
      ([k, v], i) => [k, fn(v, k, i)]
    )
  )
  
const myObject = { a: 1, b: 2, c: 3 }

console.log(objectMap(myObject, v => 2 * v)) 


3
はい、そのような問題に対する私の解決策はお勧めしません。そのため、私は別のより良いソリューションで私の答えを更新しました。
アンバーランプ2013

1
私は私の答えを更新しました-(質問を再読すると)OP は同じキーを持つ新しいオブジェクトを望んでいるようです。値は指定された関数で変更されており、元のオブジェクトを変更しないでください。
アルニタク2013

13
@KhalilRavanna私はあなたがここのコードを誤解していると思います-この答えはmap正しく実行されていないため、正しく実行されていませんreturn- 呼び出しのmapように悪用していforEachます。彼が実際に行った return myObject[value] * 2場合、結果は、元の値が2倍になったオブジェクトではなく、元の値が2倍になった配列になります。後者は明らかにOPが要求したものです。
アルニタク2014

3
@Amberlampsあなたの.reduce例は大丈夫ですが、IMHO はコールバックが機能する方法と一致する必要があるだけでなく、レキシカルスコープで利用可能であることも必要とする.mapため、Object for の利便性にはまだかなりかけ離れています。特に後者では、コールバックで関数参照を渡すだけでは不可能になり、代わりにインプレース匿名関数が必要になります。.reduce.reducemyObject
Alnitak 2015年

8
TBH私は、この回答で提示された2番目のソリューションのみを(または元々)提供したという回答をむしろ賛成したでしょう。最初のものは機能し、必ずしも間違っているわけではありませんが、他の人が述べたように、マップがそのように使用されるのを見たくありません。地図を見ると、私の心は自動的に「不変のデータ構造」を考えています
mjohnsonengr

308

単純なJSで直接変数を割り当てるワンライナーはどうですか( ES6 / ES2015)ですか?

使用を作る広がりオペレータ計算されたキー名の構文を:

let newObj = Object.assign({}, ...Object.keys(obj).map(k => ({[k]: obj[k] * obj[k]})));

jsbin

reduceを使用する別のバージョン:

let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] * obj[c]}), {});

jsbin

関数としての最初の例:

const oMap = (o, f) => Object.assign({}, ...Object.keys(o).map(k => ({ [k]: f(o[k]) })));

// To square each value you can call it like this:
let mappedObj = oMap(myObj, (x) => x * x);

jsbin

ネストされたオブジェクトを関数スタイルで再帰的にマッピングする場合は、次のように行うことができます。

const sqrObjRecursive = obj =>
  Object.keys(obj).reduce(
    (newObj, key) =>
      obj[key] && typeof obj[key] === "object"
        ? { ...newObj, [key]: sqrObjRecursive(obj[key]) } // recurse.
        : { ...newObj, [key]: obj[key] * obj[key] }, // square val.
    {}
  );       

jsbin

または、次のようにさらに命令的に:

const sqrObjRecursive = obj => {
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === "object") obj[key] = sqrObjRecursive(obj[key]);
    else obj[key] = obj[key] * obj[key];
  });
  return obj;
};

jsbin

ES7 / ES2016以降Object.entries()Object.keys()たとえば次のように使用できます。

let newObj = Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v})));

ES2019が導入されましたObject.fromEntries()。これにより、これがさらに簡素化されます。

let newObj = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]));


継承されたプロパティとプロトタイプチェーン:

まれに、継承されたオブジェクトのプロパティをプロトタイプチェーンに保持するクラスのようなオブジェクトをマップする必要がある場合があります。は継承されたプロパティを列挙しないため、このような場合は機能しません。継承されたプロパティをマップする必要がある場合は、を使用する必要があります。Object.keys()Object.keys()for (key in myObj) {...}

次に、別のオブジェクトのプロパティを継承するオブジェクトの例と、Object.keys()そのようなシナリオで機能しない例を示します。

const obj1 = { 'a': 1, 'b': 2, 'c': 3}
const obj2 = Object.create(obj1);  // One of multiple ways to inherit an object in JS.

// Here you see how the properties of obj1 sit on the 'prototype' of obj2
console.log(obj2)  // Prints: obj2.__proto__ = { 'a': 1, 'b': 2, 'c': 3}

console.log(Object.keys(obj2));  // Prints: an empty Array.

for (key in obj2) {
  console.log(key);              // Prints: 'a', 'b', 'c'
}

jsbin

ただし、継承を避けてください。:-)


1
美しいですが、Object.keys継承されたプロパティを列挙しないという事実に気づきました。警告を追加することをお勧めします。
David Braun

答えを短くするには、を使用してくださいObject.entries({a: 1, b: 2, c: 3})

1
あなたはいくつかのタイプミスがあります((o, f)引数として参照しますがobj、本文で使用します
。– kzahel

4
ええ、次の人に数分かけてこの機能が何であるかを理解させてください :)
Andrey Popov

1
が空のオブジェクトのObject.assign(...Object.entries(obj).map(([k, v]) => ({[k]: v * v})))場合、ソリューションは機能しませんobj。に変更しますObject.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v})))
blackcatweb

117

ネイティブメソッドはありませんが、lodash#mapValuesは見事にその仕事をします

_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }

6
1つの関数だけのために、追加のHTTP呼び出しと追加のライブラリを追加する必要はありません。いずれにせよ、この回答は古くなりObject.entries({a: 1, b: 2, c: 3})、配列を取得するために呼び出すことができます。

11
なぜ配列が役立つのですか?要件は、マップされたオブジェクトでした。また、本質的にこの種の目的のために構築された人気のあるライブラリであるLodashを使用した暗黙のHTTP呼び出しはありません。
corse32 2017

2
Lodashをプルするための追加のHTTP呼び出し、私は推測します。これが唯一のユースケースである場合、実際には過剰です。それにもかかわらず、Lodashがこのように広く普及しているライブラリであるので、この答えを出すのは理にかなっています。
igorsantos07 2018年

2
言うまでもなく、実際に使用_.map()する必要があるため、2番目の引数としてキーを取得します-OPの必要に応じて。
igorsantos07 2018年

_.mapValues()2番目の引数としてキーも提供します。また、_.map()それが唯一の配列を返すよう、ここで働かない、ないオブジェクト:_.map({ 'a': 1, 'b': 2, 'c': 3}, n => n * 3) // [3, 6, 9]
MeltedPenguinの

57

書くのはとても簡単です:

Object.map = function(o, f, ctx) {
    ctx = ctx || this;
    var result = {};
    Object.keys(o).forEach(function(k) {
        result[k] = f.call(ctx, o[k], k, o); 
    });
    return result;
}

コード例:

> o = { a: 1, b: 2, c: 3 };
> r = Object.map(o, function(v, k, o) {
     return v * v;
  });
> r
{ a : 1, b: 4, c: 9 }

注:このバージョンthisでは、コールバックのコンテキストを(オプションで)設定することもできます。Arrayメソッド。

編集 - オブジェクトのObject.prototype名前が付いmapた既存のプロパティと衝突しないように、の使用を削除するように変更されました。


3
OK。:) この質問へのコメントは私をここにもたらしました。受け入れられた答えは明らかに誤用されているので、これを+1 しますが、たとえ列挙可能でなくてもmap、変更を行うとObject.prototypeうまくいかないと言わざるを得ません。
JLRishe 2015年

2
@JLRisheありがとう。ES5のIMHOは、他の方法との衝突の(理論的な)リスク以外に、変更しない理由もはやありませんObject.prototype。FWIW、私は他の回答がどうして多くの票を獲得したか理解できません、それは完全に間違っています。
Alnitak、2015年

7
@JLRisheあなたは古いギーザです...時代を乗り越えて、そのプロトタイプを修正してください!
Nick Manning

12
@NickManningあなたは若いホイッパースナッパーとあなたの無責任な行動。芝生から降りろ!
JLRishe 2015年

3
これはすでに機能しません:o = {map: true, image: false}。代わりに書くだけでリスクを冒していますo.map(fn)map(o,fn)
fregante

25

あなたは使用することができObject.keys、その後とforEachキーの返された配列の上に:

var myObject = { 'a': 1, 'b': 2, 'c': 3 },
    newObject = {};
Object.keys(myObject).forEach(function (key) {
    var value = myObject[key];
    newObject[key] = value * value;
});

または、よりモジュール化された方法で:

function map(obj, callback) {
    var result = {};
    Object.keys(obj).forEach(function (key) {
        result[key] = callback.call(obj, obj[key], key, obj);
    });
    return result;
}

newObject = map(myObject, function(x) { return x * x; });

Object.keysオブジェクト自身の列挙可能なプロパティのみを含む配列を返すためfor..inhasOwnPropertyチェック付きのループのように動作することに注意してください。


20

これはまっすぐなbsであり、JSコミュニティの誰もがそれを知っています。この機能があるはずです:

const obj1 = {a:4, b:7};
const obj2 = Object.map(obj1, (k,v) => v + 5);

console.log(obj1); // {a:4, b:7}
console.log(obj2); // {a:9, b:12}

ここにナイーブな実装があります:

Object.map = function(obj, fn, ctx){

    const ret = {};

    for(let k of Object.keys(obj)){
        ret[k] = fn.call(ctx || null, k, obj[k]);
    });

    return ret;
};

これを常に自分で実装しなければならないのは非常に迷惑です;)

Objectクラスに干渉しない、もう少し洗練されたものが必要な場合は、以下を試してください。

let map = function (obj, fn, ctx) {
  return Object.keys(obj).reduce((a, b) => {
    a[b] = fn.call(ctx || null, b, obj[b]);
    return a;
  }, {});
};


const x = map({a: 2, b: 4}, (k,v) => {
    return v*2;
});

ただし、このマップ関数をObjectに追加しても安全です。Object.prototypeには追加しないでください。

Object.map = ... // fairly safe
Object.prototype.map ... // not ok

なぜこの関数をグローバルObjectオブジェクトに追加するのですか?ただconst mapObject = (obj, fn) => { [...]; return ret; }
アンバーランプ2016年

7
Object :)のメソッドである必要があるため
Alexander Mills

1
Object.prototypeをいじらない限り、問題ありません
Alexander Mills

1
地図がランダムで曖昧なものだったら、きっと。しかし.map、一般にFunctorインターフェースとして理解されており、独自の非常に明確に定義された.mapインターフェース/ロジックを持つものを含め、JavaScriptのほとんどすべてがObjectになる可能性があるため、「オブジェクト」のFunctorの単一の定義はありません。
Dtipson 2017

1
私の意見では、コールバックの最初の引数は、キーではなく反復要素自体である必要があります。たとえば、通常は共通のマップで発生するので、同じオブジェクトを提供する必要があると想定します:Object.map({prop:1}、el => el)しかし、同じキー名の値を持つキーを返します...
Gleb Dolzikov

17

オブジェクトを配列にマッピングするための検索と回答を求めてここに来て、結果としてこのページを取得しました。私と同じ答えを探してここに来た場合は、配列にオブジェクトをマッピングしてオブジェクトを作成する方法を次に示します。

mapを使用して、次のようにオブジェクトから新しい配列を返すことができます。

var newObject = Object.keys(myObject).map(function(key) {
   return myObject[key];
});

6
これはリモートでは機能しません。新しいオブジェクトではなく、オブジェクトの値の配列を返すだけです。私はそれがこれだけ多くの賛成票を持っているとは信じられません。
アルニタク

1
正解です。オブジェクトとオブジェクトをマッピングするための回答を探していたため、質問に正しく回答していません。オブジェクトを配列にマッピングするための回答を探してここに来て、結果としてこのページが表示されたので、私の回答を残して編集し、結果としてここに来た可能性がある人々のための代替回答であることを反映しますオブジェクトの配列へのマッピングを探します。
JoeTron

5
ES7でそれは自明であろうObject.values(myObject)
アルニタク

1
知っておくと便利ですが、一部の人々は依然としてレガシーコードを使用する必要があります。
JoeTron

const obj = {foo: 'bar'、baz:42}; console.log(Object.entries(obj)); // [['foo'、 'bar']、['baz'、42]]
バシルプール

12

受け入れられた回答には2つの欠点があります。

  • それは誤用します Array.prototype.reduce削減は複合タイプの構造を変更する手段を意味するため、れます。これは、この場合には起こりません。
  • 特に再利用できない

ES6 / ES2015機能的アプローチ

すべての関数はカレー形式で定義されていることに注意してください。

// small, reusable auxiliary functions

const keys = o => Object.keys(o);

const assign = (...o) => Object.assign({}, ...o);

const map = f => xs => xs.map(x => f(x));

const mul = y => x => x * y;

const sqr = x => mul(x) (x);


// the actual map function

const omap = f => o => {
  o = assign(o); // A
  map(x => o[x] = f(o[x])) (keys(o)); // B
  return o;
};


// mock data

const o = {"a":1, "b":2, "c":3};


// and run

console.log(omap(sqr) (o));
console.log(omap(mul(10)) (o));

  • 行A oが再割り当てされます。Javascriptはを共有することで参照値を渡すため、の浅いコピーoが生成されます。私たちは今、変異させることができますoomap変異なしo親スコープで変更する、ました。
  • 行B mapでは、の変更をmap実行するため、戻り値は無視されoます。この副作用omapは親スコープ内にとどまり、表示されないため、完全に許容されます。

これは最速のソリューションではなく、宣言的で再利用可能なソリューションです。以下は、1行の同じ実装ですが、簡潔ですが読みにくくなっています。

const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o);

補遺-オブジェクトがデフォルトで反復できないのはなぜですか?

ES2015はイテレーターとイテラブルプロトコルを指定しました。ただし、オブジェクトはまだ反復可能ではないため、マップできません。その理由は、データとプログラムレベルの混在です


1
少しやりすぎたように見えます。私は本質的にループであるもののために6つの関数を数えます。他の解決策ははるかに単純であり、受け入れられた答えも5倍速くなっています。
fregante 16

4
@ bfred.itコメントの目的は何ですか?マイクロ最適化が好きなら、Array.prototype.reduceどちらも使用すべきではありません。ここで私が説明したいのは、受け入れられた答えが何らかの形で「乱用」されArray.prototype.reduce、純粋に機能的なマッピングを実装する方法です。関数型プログラミングに興味がない場合は、私の答えを無視してください。

2
「複合型の構造を変更する手段を減らすこと」私はそうは思いません。正確に同じ長さまたはまったく同じ値の新しい配列を生成するArray.reduceは完全に正当です。リデュースは、マップやフィルターの機能を再現するために使用できます(または複雑なトランスデューサーのように、両方がリデュースの基本です)。FPでは、それは折り目のタイプであり、同じタイプで終わる折り目はまだ折り目です。
Dtipson 2017

2
@Dtipsonあなたは絶対的に正しいです。A foldmap(ファンクタ)よりも一般的です。しかし、これは2016年半ばからの私の知識でした。それは永遠の半分です:D

1
ハ、うん。このすべてが相互にどのように相互接続されているか、そして用語とインターフェースのより広いエコシステムに気付くまでに何年もかかりました。とても簡単に思えるようなことは、信じられないほど複雑であることが判明し、信じられないほど複雑であるように見えるいくつかのものは、非常にきれいに抽象化されています。:)
Dtipson 2017

10

JavaScriptが新しいObject.fromEntriesメソッドを取得しました。

function mapObject (obj, fn) {
  return Object.fromEntries(
    Object
      .entries(obj)
      .map(fn)
  )
}

const myObject = { a: 1, b: 2, c: 3 }
const myNewObject = mapObject(myObject, ([key, value]) => ([key, value * value]))
console.log(myNewObject)

説明

上記のコードは、オブジェクトをネスト可能な配列([[<key>,<value>], ...])に変換します。Object.fromEntries配列をオブジェクトに変換します。

このパターンのすばらしい点は、マッピング時にオブジェクトキーを簡単に考慮できるようになったことです。

ドキュメンテーション

ブラウザのサポート

Object.fromEntries現在、これらのブラウザ/エンジンでのみサポートされていますで、利用可能なポリフィルがあります(例:@ babel / polyfill)。


2
基本的にObject.fromEntries、はの逆ですObject.entriesObject.entriesオブジェクトをキーと値のペアのリストに変換します。Object.fromEntriesキーと値のペアのリストをオブジェクトに変換します。
orad

8

最小バージョン(es6):

Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})

これはタイプミスが原因で間違っていると思います。つまり、Object.entries(obj).reduce((a, [k, v]) => [a[k] = v * v, a], {})括弧の代わりに山括弧(=> )を意味し ます。
Alexander Mills

@AlexanderMills、私のコードは正しいです。括弧内のコンマ演算子についてさらに読む:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Yukulélé

わかりました。次に説明したいと思うかもしれませんが、それがコメントの目的だと思います。
Alexander Mills

1
基本的に、私のコードは次と同等ですObject.entries(obj).reduce((a, [k, v]) => {a[k] = v * v; return a}, {})
。–ユクレレ

1
すばらしい答えです。Object.entries().reduceそれが使用されている場合ES6 + .Wouldと私は考えて最善の解決策は、より多くのupvotesを取得しているreturnのではなく減速で声明をimplicit returnし、comma私見読みきちんとしたが難しい-オペレータ
Drenai

7

最大のパフォーマンスを得るために。

オブジェクトが頻繁に変更されないが、頻繁に繰り返す必要がある場合は、ネイティブマップをキャッシュとして使用することをお勧めします。

// example object
var obj = {a: 1, b: 2, c: 'something'};

// caching map
var objMap = new Map(Object.entries(obj));

// fast iteration on Map object
objMap.forEach((item, key) => {
  // do something with an item
  console.log(key, item);
});

Object.entriesはすでにChrome、Edge、Firefox、ベータ版Operaで動作するため、将来性のある機能です。これはES7からのものであるため、IEが機能しない場合はhttps://github.com/es-shims/Object.entriesにポリフィルします


どのように使用する方法について消滅を
КонстантинВан

1
@ K._以前は非常に低速でしたが、現在のパフォーマンスについてはわかりません。V8 v 5.6は構造化解除の
Pawel

それとも、あまりパフォーマンスES7が、不変:const fn = v => v * 2; const newObj = Object.entries(myObject).reduce((acc, [k,v]) => Object.assign({}, acc, {[k]: fn(v)}), {});
mikebridge

他のどの回答もObject.entriesを使用しないのはなぜでしょうか。キーを繰り返し処理してobj [key]を使用するよりもはるかにクリーンです。
corwin.amber

6

mapメソッドとforEach配列を使用できますが、それを使用したい場合Object、あなたは以下のようなひねりを加えた、それを使用することができます。

JavaScript(ES6)の使用

var obj = { 'a': 2, 'b': 4, 'c': 6 };   
Object.entries(obj).map( v => obj[v[0]] *= v[1] );
console.log(obj); //it will log as {a: 4, b: 16, c: 36}

var obj2 = { 'a': 4, 'b': 8, 'c': 10 };
Object.entries(obj2).forEach( v => obj2[v[0]] *= v[1] );
console.log(obj2); //it will log as {a: 16, b: 64, c: 100}

jQueryの使用

var ob = { 'a': 2, 'b': 4, 'c': 6 };
$.map(ob, function (val, key) {
   ob[key] *= val;
});
console.log(ob) //it will log as {a: 4, b: 16, c: 36}

または$.each、以下の例のように、メソッドのような他のループも使用できます。

$.each(ob,function (key, value) {
  ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}

$jqueryはどこですか?
masterxilo 2018年

はい、$とjQueryの両方を使用できます
Haritsinh Gohil 2018年

1
@ロナルド私は誤解しました、私の最新の回答を参照してください。投票に反対するのではなく、本当に間違いを認識してくれて、コミュニティの改善に感謝しています。
Haritsinh Gohil

@bcoughlanはい、それは私がコメントで書いたものでもあります、それは数を二乗します、それであなたは何を望みますか?
Haritsinh Gohil

1
@HaritsinhGohil私は、jQueryソリューションを提供したことで彼があなたを批判しているという意味だと思いました。最近はjQueryから離れる勢いがあり、もちろんNodeもありますが、OPが純粋なJSまたはNodeを明確に要求しない限り、誰かがWeb環境でそのjQueryを使用していると仮定しても問題ないと思います利用できることはほぼ確実です。
abalter

4

map functionは存在しませんが、そのObject.prototypeようにエミュレートできます

var myMap = function ( obj, callback ) {

    var result = {};

    for ( var key in obj ) {
        if ( Object.prototype.hasOwnProperty.call( obj, key ) ) {
            if ( typeof callback === 'function' ) {
                result[ key ] = callback.call( obj, obj[ key ], key, obj );
            }
        }
    }

    return result;

};

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

var newObject = myMap( myObject, function ( value, key ) {
    return value * value;
});

パフォーマンスの問題:typeof callback === 'function'完全に冗長です。1)mapコールバックなしでは意味callbackがありません。2)が機能しない場合、map実装は無意味なforループで実行されます。また、Object.prototype.hasOwnProperty.call( obj, key )少しやりすぎです。
ankhzet 2016

3

私はこれを学習しようとしているグーグル検索の最初の項目としてこれに出くわし、他の人々がこれを最近見つけた解決策を共有することを考えました。

immutableは独自のドキュメントでOPの正確な状況を使用しているため、共有することは興味深いと思います。以下は自分のコードではなく、現在のimmutable-jsドキュメントから抜粋したものです。

const { Seq } = require('immutable')
const myObject = { a: 1, b: 2, c: 3 }
Seq(myObject).map(x => x * x).toObject();
// { a: 1, b: 4, c: 9 } 

Seqに他のプロパティがないこと(「Seqは遅延操作を記述しているため、中間コレクションを作成しないことにより、すべての高次コレクションメソッド(マップやフィルターなど)の使用を効率的に連鎖させることができます」)およびその他の不変のjsデータ構造物も非常に効率的に仕事をするかもしれません。

この方法を使用する人はもちろん、 npm install immutableドキュメントを読む必要があります。

https://facebook.github.io/immutable-js/


3

編集:新しいJavaScript機能を使用する標準的な方法は-

const identity = x =>
  x

const omap = (f = identity, o = {}) =>
  Object.fromEntries(
    Object.entries(o).map(([ k, v ]) =>
      [ k, f(v) ]
    )
  )

どこoかにオブジェクトfがあり、マッピング関数です。または、fromの関数a -> bとtypeの値をa持つオブジェクトを指定すると、typeの値を持つオブジェクトを生成することができますb。疑似型シグネチャとして-

// omap : (a -> b, { a }) -> { b }

元の回答は強力なコンビネーターを実証するために書かれており、mapReduceこれにより私たちは自分の変革を別の方法で考えることができます

  1. mマッピング機能–前に着信要素を変換する機会を与えます…
  2. rレデューシング関数–この関数は、アキュムレータをマップされた要素の結果と組み合わせます

直感的に、にmapReduce直接接続できる新しいレデューサーを作成しArray.prototype.reduceます。しかし、もっと重要なのは、我々は我々のオブジェクト数子の実装を実現することができomap、オブジェクトモノイドを利用することにより分かりやすく、Object.assignそして{}

const identity = x =>
  x
  
const mapReduce = (m, r) =>
  (a, x) => r (a, m (x))

const omap = (f = identity, o = {}) =>
  Object
    .keys (o)
    .reduce
      ( mapReduce
          ( k => ({ [k]: f (o[k]) })
          , Object.assign
          )
      , {}
      )
          
const square = x =>
  x * x
  
const data =
  { a : 1, b : 2, c : 3 }
  
console .log (omap (square, data))
// { a : 1, b : 4, c : 9 }

実際に記述しなければならないプログラムの唯一の部分は、マッピングの実装自体です。

k => ({ [k]: f (o[k]) })

つまり、既知のオブジェクトoといくつかのキーを指定してk、オブジェクトとその計算されたプロパティを構築しますkつまりはfキーの値を呼び出した結果ですo[k]

私たちは一瞥します mapReduce最初に抽象化すると、のシーケンスの可能性をoreduce

// oreduce : (string * a -> string * b, b, { a }) -> { b }
const oreduce = (f = identity, r = null, o = {}) =>
  Object
    .keys (o)
    .reduce
      ( mapReduce
          ( k => [ k, o[k] ]
          , f
          )
      , r
      )

// omap : (a -> b, {a}) -> {b}
const omap = (f = identity, o = {}) =>
  oreduce
    ( mapReduce
        ( ([ k, v ]) =>
            ({ [k]: f (v) })
        , Object.assign
        )
    , {}
    , o
    )

すべてが同じように機能しますが、 omap、より高いレベルで定義できるようになりました。もちろん、新しいものObject.entriesはばかげているように見えますが、練習問題は学習者にとって依然として重要です。

mapReduceここでは十分な可能性は見えませんが、適用できる場所の数を確認するのは興味深いので、この回答を共有します。それがどのように派生するか、そしてそれが役立つ他の方法に興味があるなら、この答えを見ください。


4
では、Mapandを使用してみましょうMap.entries。プレーンオブジェクトをマップデータタイプとして乱用することにうんざりしています。

2
私は同意するMapところ/、可能な場合に使用する必要がありますが、それは完全に無地のJSにこれらの手順を使用する必要性を置き換えるものではありませんカントーオブジェクト:D
ありがとう

2

@Amberlampsの回答に基づいて、ユーティリティ関数を以下に示します(コメントとして醜く見えました)。

function mapObject(obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, value) {
        newObj[value] = mapFunc(obj[value]);
        return newObj;
    }, {});
}

そして使用は:

var obj = {a:1, b:3, c:5}
function double(x){return x * 2}

var newObj = mapObject(obj, double);
//=>  {a: 2, b: 6, c: 10}

2
var myObject = { 'a': 1, 'b': 2, 'c': 3 };


Object.prototype.map = function(fn){
    var oReturn = {};
    for (sCurObjectPropertyName in this) {
        oReturn[sCurObjectPropertyName] = fn(this[sCurObjectPropertyName], sCurObjectPropertyName);
    }
    return oReturn;
}
Object.defineProperty(Object.prototype,'map',{enumerable:false});





newObject = myObject.map(function (value, label) {
    return value * value;
});


// newObject is now { 'a': 1, 'b': 4, 'c': 9 }

enumerableデフォルトはfalse
ありがとう

2

私の応答は、ここで最も高い評価された応答に主に基づいています。うまくいけば、誰もが理解します(私のGitHubでも同じ説明があります)。これが、彼の地図への取り組みが機能する理由です。

Object.keys(images).map((key) => images[key] = 'url(' + '"' + images[key] + '"' +    
')');

この関数の目的は、オブジェクトを取得し、配列を返さずにすべてのオブジェクト(オブジェクトと配列)で使用できるメソッドを使用して、オブジェクトの元の内容を変更することです。JS内のほとんどすべてがオブジェクトであり、そのため、継承のパイプラインのさらに下の要素は、技術的に使用可能なもの(およびその逆)を技術的に使用できます。

これが機能する理由は、既存のオブジェクトを単に変更する代わりに、配列の明示的または暗黙的なRETURNを提供するREQUIRING配列を返す.map関数が原因です。基本的にプログラムをだまして、オブジェクトが配列であると考えるようにします。これにより、個々のキーが関連付けられている値に作用するマップ関数を使用できるようになります(実際には誤って配列を返しましたが、修正しました)。通常の意味での戻りがない限り、元のオブジェクトをそのまま使用して作成され、プログラムどおりに変更された配列はありません。

この特定のプログラムは、イメージと呼ばれるオブジェクトを取得し、そのキーの値を取得して、別の関数内で使用するためにurlタグを追加します。オリジナルはこれです:

var images = { 
snow: 'https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305', 
sunny: 'http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-east-   
Matanzas-city- Cuba-20170131-1080.jpg', 
rain: 'https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg' };

...そして変更されたのはこれです:

var images = { 
snow: url('https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305'),     
sunny: url('http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-   
east-Matanzas-city- Cuba-20170131-1080.jpg'), 
rain: url('https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg') 
};

オブジェクトの元の構造はそのまま残り、戻り値がない限り、通常のプロパティアクセスが可能です。通常のように配列を返さないでください。すべて問題ありません。目標は、元の値(images [key])を必要なものに再割り当てし、それ以外のものには再割り当てしないことです。私が知る限り、配列の出力を防ぐために、イメージ[キー]の再割り当てが必要であり、配列を返す暗黙的または明示的な要求はありません(変数の割り当てでこれが行われ、問題が発生していました)。

編集:

元のオブジェクトの変更を回避するために、新しいオブジェクトの作成に関する彼の他の方法に対処する予定です(そして、誤って配列を出力として作成するのを回避するために、再割り当てが依然として必要であるようです)。これらの関数は矢印構文を使用し、将来使用するために新しいオブジェクトを作成したいだけの場合に使用します。

const mapper = (obj, mapFn) => Object.keys(obj).reduce((result, key) => {
                result[key] = mapFn(obj)[key];
                return result;
            }, {});

var newImages = mapper(images, (value) => value);

これらの関数の動作方法は次のとおりです。

mapFnは、後で追加される関数(この場合(値)=>値)を取り、そこに格納されているものをそのキーの値として返すか、またはmapFn( obj)[key]、

次に、result [key] = mapFn(obj)[key]のキーに関連付けられた元の値を再定義します

そして、結果に対して実行された操作を返します(.reduce関数の最後にある括弧内にあるアキュムレータ)。

これはすべて、選択したオブジェクトに対して実行されており、返された配列に対する暗黙の要求はまだありません。私が知る限り、値を再割り当てする場合にのみ機能します。これには、いくつかの頭の体操が必要ですが、上記のように必要なコード行が減ります。出力は次のようにまったく同じです。

{snow: "https://www.trbimg.com/img-5aa059f5/turbine/bs-   
md-weather-20180305", sunny: "http://www.cubaweather.org/images/weather-
photos/lmorning-east-Matanzas-city-Cuba-20170131-1080.jpg", rain: 
"https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg"}

これはNON-NUMBERSで機能することを覚えておいてください。mapFN関数で値を単純に返すことで、任意のオブジェクトを複製できます。


2

最初の関数は質問に答えます。地図を作成します。

2番目の関数はマップを作成しません。代わりに、を使用して既存のオブジェクトのプロパティをループします hasOwnProperty()。この代替マップは、状況によってはより良い解決策になる場合があります。

var myObject = { 'a': 1, 'b': 2, 'c': 3 }


//creates a map (answers question, but function below is 
//much better in some situations)
var newObject = {};
makeMap();    
function makeMap() {
    for (var k in myObject) {
        var value = myObject[k];
        newObject[k] = value * value;
    }
    console.log(newObject); //mapped array
}


//Doesn't create a map, just applies the function to
//a specific property using existing data
function getValue(key) {
  for (var k in myObject) {
    if (myObject.hasOwnProperty(key)) {
      var value = myObject[key]
      return value * value; //stops iteration
    }
  }
}
Input: <input id="input" value="" placeholder="a, b or c"><br>
Output:<input id="output"><br>
<button onclick="output.value=getValue(input.value)" >Get value</button>


2
これは元の質問には答えません-提供された関数を使用して値を新しいオブジェクトにマッピングしていません(つまり、これはArray.prototype.mapのようなものではありません)。
Matt Browne

@MattBrowne私は間違いなくマップであるものを作成する2番目のメソッドを追加しました。遅いですが、OPが要求することを実行します。最初の方法は多くの状況で理想的です。そのため、ループとプロパティ関数を使用しました。
ビクターストッダード2018年

1

map値だけでなくキーもping することに興味がある場合Object.map(valueMapper, keyMapper)は、次のように動作します。

var source = { a: 1, b: 2 };
function sum(x) { return x + x }

source.map(sum);            // returns { a: 2, b: 4 }
source.map(undefined, sum); // returns { aa: 1, bb: 2 }
source.map(sum, sum);       // returns { aa: 2, bb: 4 }

3
あなたは基本的に答えとしてリンクを提供しただけだと思いますが、それは眉をひそめています。
Chris Wright

誰にとっても役立つ場合:npm install @mattisg/object.map
MattiSG 2016年

1

(@Amberlampsと@yonatanmnの回答に基づいて)キーも変更できるバージョンが必要でした。

var facts = [ // can be an object or array - see jsfiddle below
    {uuid:"asdfasdf",color:"red"},
    {uuid:"sdfgsdfg",color:"green"},
    {uuid:"dfghdfgh",color:"blue"}
];

var factObject = mapObject({}, facts, function(key, item) {
    return [item.uuid, {test:item.color, oldKey:key}];
});

function mapObject(empty, obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, key) {
        var kvPair = mapFunc(key, obj[key]);
        newObj[kvPair[0]] = kvPair[1];
        return newObj;
    }, empty);
}

factObject =

{
"asdfasdf": {"color":"red","oldKey":"0"},
"sdfgsdfg": {"color":"green","oldKey":"1"},
"dfghdfgh": {"color":"blue","oldKey":"2"}
}

編集:開始オブジェクト{}を渡すためのわずかな変更。[]にすることができます(キーが整数の場合)


1

具体的には、単一のオブジェクトの配列に使用していたのと同じ関数を使用し、それを単純にしたかったのです。これは私のために働きました:

var mapped = [item].map(myMapFunction).pop();

これは何も変わらないvar mapped = myMapFunction(item)
ありがとう

これは、私がここで見た他のすべての「ロケット科学」ソリューションよりも非常に単純です。共有してくれてありがとう:)
Yuri Teixeira

1

OPが正確に要求したものにより厳密に応答するために、OPはオブジェクトを必要としています。

myObject = { 'a': 1, 'b': 2, 'c': 3 }

mapメソッドを持つにはmyObject.map

次のように使用されるArray.prototype.mapに似ています。

newObject = myObject.map(function(value、label){
    戻り値*値;
});
// newObjectが{'a':1、 'b':4、 'c':9}になりました

私見(「に換算して測定最高頼まれているものに近い +「全くES {5,6,7}は不必要ありません」」)の答えは次のようになります。

myObject.map = function mapForObject(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property) && property != "map"){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

上記のコードは、最近のECMAScriptエディションでのみ利用可能な言語機能の意図的な使用を回避します。上記のコードを使用すると、この問題を解決できます。

myObject = { 'a': 1, 'b': 2, 'c': 3 };

myObject.map = function mapForObject(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property) && property != "map"){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

newObject = myObject.map(function (value, label) {
  return value * value;
});
console.log("newObject is now",newObject);
ここに代替テストコード

いくつかの人に不快感を与えるだけでなく、このようなプロトタイプチェーンにソリューションを挿入することも可能です。

Object.prototype.map = function(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property)){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

何か、注意深い監視で行われた場合map、他のオブジェクト(つまり、配列map)のメソッドに影響を与えたり、悪影響を与えたりしてはなりません。



1

関数mapEntriesを定義します。

mapEntriesは、パラメータ値、キー、オブジェクトを使用して、オブジェクトの各エントリで呼び出されるコールバック関数を取ります。新しい値を返す必要があります。

mapEntriesは、コールバックから返された新しい値を持つ新しいオブジェクトを返す必要があります。

Object.defineProperty(Object.prototype, 'mapEntries', {
  enumerable: false,
  value: function (mapEntriesCallback) {
    return Object.fromEntries(
      Object.entries(this).map(
        ([key, value]) => [key, mapEntriesCallback(value, key, this)]
      )
    )
  }
})


// Usage example:

var object = {a: 1, b: 2, c: 3}
var newObject = object.mapEntries(value => value * value)
console.log(newObject)
//> {a: 1, b: 4, c: 9}

編集: 以前のバージョンでは、これが列挙可能なプロパティではないことを指定していませんでした


0

ちょっと役立つかもしれない小さなマッパー関数を書いた。

    function propertyMapper(object, src){
         for (var property in object) {   
           for (var sourceProp in src) {
               if(property === sourceProp){
                 if(Object.prototype.toString.call( property ) === '[object Array]'){
                   propertyMapper(object[property], src[sourceProp]);
                   }else{
                   object[property] = src[sourceProp];
                }
              }
            }
         }
      }

1
あなたは「ねえ」ではなく「私」を意味していたと思います
Carlo

0

別の考えは、ディープオブジェクトでも機能するカスタムjson stringify関数を使用することです。とにかくjsonとしてサーバーに投稿する場合に便利です。

const obj = { 'a': 1, 'b': 2, x: {'c': 3 }}
const json = JSON.stringify(obj, (k, v) => typeof v === 'number' ? v * v : v)

console.log(json)
console.log('back to json:', JSON.parse(json))


0

文字列のみを処理して免除を減らします。

Object.keys(params).map(k => typeof params[k] == "string" ? params[k] = params[k].trim() : null);

0

TypeScriptのオブジェクトマッパー

私の使用例などのObject.fromEntriesような、この1、それでも、彼らは非常に使いやすいではありません。を使用Object.keysしてから検索する回答keyは、実際には必要のない可能性のある複数の検索を行っています。

Object.map関数があればよかったのですが、独自の関数を作成して、次のobjectMap両方を変更する機能で呼び出すことができます。keyvalue

使用法(JavaScript):

const myObject = { 'a': 1, 'b': 2, 'c': 3 };

// keep the key and modify the value
let obj = objectMap(myObject, val => val * 2);
// obj = { a: 2, b: 4, c: 6 }


// modify both key and value
obj = objectMap(myObject,
    val => val * 2 + '',
    key => (key + key).toUpperCase());
// obj = { AA: '2', BB: '4', CC: '6' }

コード(TypeScript):

interface Dictionary<T> {
    [key: string]: T;
}

function objectMap<TValue, TResult>(
    obj: Dictionary<TValue>,
    valSelector: (val: TValue, obj: Dictionary<TValue>) => TResult,
    keySelector?: (key: string, obj: Dictionary<TValue>) => string,
    ctx?: Dictionary<TValue>
) {
    const ret = {} as Dictionary<TResult>;
    for (const key of Object.keys(obj)) {
        const retKey = keySelector
            ? keySelector.call(ctx || null, key, obj)
            : key;
        const retVal = valSelector.call(ctx || null, obj[key], obj);
        ret[retKey] = retVal;
    }
    return ret;
}

TypeScriptを使用していない場合は、上記のコードをTypeScript PlaygroundにコピーしてJavaScriptコードを取得します。

また、パラメーターリストでkeySelector後から付けた理由valSelectorは、それがオプションであるためです。

*一部のクレジットはalexander-millsの回答に使用されます。


0
const mapObject = (targetObject, callbackFn) => {
    if (!targetObject) return targetObject;
    if (Array.isArray(targetObject)){
        return targetObject.map((v)=>mapObject(v, callbackFn))
    }
    return Object.entries(targetObject).reduce((acc,[key, value]) => {
        const res = callbackFn(key, value);
        if (!Array.isArray(res) && typeof res ==='object'){
            return {...acc, [key]: mapObject(res, callbackFn)}
        }
        if (Array.isArray(res)){
            return {...acc, [key]: res.map((v)=>mapObject(v, callbackFn))}
        }
        return {...acc, [key]: res};
    },{})
};
const mapped = mapObject(a,(key,value)=> {
    if (!Array.isArray(value) && key === 'a') return ;
    if (!Array.isArray(value) && key === 'e') return [];
    if (!Array.isArray(value) && key === 'g') return value * value;
    return value;
});
console.log(JSON.stringify(mapped)); 
// {"b":2,"c":[{"d":2,"e":[],"f":[{"g":4}]}]}

この関数は、オブジェクトとオブジェクトの配列を再帰的に処理します。未定義で返された場合、属性を削除できます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.