Kartenfunktion für Objekte (anstelle von Arrays)

1065

Ich habe ein Objekt:

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

Ich suche nach einer nativen Methode, die der folgenden ähnelt Array.prototype.map:

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

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

Hat JavaScript eine solche mapFunktion für Objekte? (Ich möchte dies für Node.JS, daher interessieren mich browserübergreifende Probleme nicht.)

Zufälliges Blau
quelle
1
Die meisten Antworten verwenden Object.keys, die keine genau definierte Reihenfolge haben. Das kann problematisch sein, ich schlage Object.getOwnPropertyNamesstattdessen vor.
Oriol
14
Erstaunlich für mich, dass JS diese unglaublich rudimentäre Aufgabe nicht übernommen hat.
Jchook
3
@Oriol bist du dir da sicher? Gemäß den MDN-Webdokumenten ist die Reihenfolge der Array-Elemente zwischen Object.keysund konsistent Object.getOwnPropertyNames. Siehe developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Bart
@Bart Die Reihenfolge Object.keysist implementierungsabhängig. Siehe stackoverflow.com/a/30919039/1529630
Oriol
4
@Oriol Du solltest dich sowieso nicht auf die Reihenfolge der Schlüssel in Objekten verlassen, daher ist das für die Frage irgendwie irrelevant - da er nie angegeben hat, dass die Reihenfolge für ihn von Bedeutung ist. Und wenn ihm die Ordnung wichtig war, sollte er sowieso kein Objekt benutzen.
BT

Antworten:

1541

mapDas ObjectObjekt ist nicht nativ , aber wie wäre es damit:

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 }

Sie können jedoch problemlos über ein Objekt iterieren, indem Sie Folgendes verwenden 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 }

Aktualisieren

Viele Leute erwähnen, dass die vorherigen Methoden kein neues Objekt zurückgeben, sondern das Objekt selbst bearbeiten. Im Übrigen wollte ich eine weitere Lösung hinzufügen, die ein neues Objekt zurückgibt und das ursprüngliche Objekt unverändert lässt:

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.reducereduziert ein Array auf einen einzelnen Wert, indem der vorherige Wert mit dem aktuellen Wert zusammengeführt wird. Die Kette wird durch ein leeres Objekt initialisiert {}. Bei jeder Iteration wird ein neuer Schlüssel von myObjectmit dem doppelten Schlüssel als Wert hinzugefügt.

Aktualisieren

Mit den neuen ES6-Funktionen können Sie eleganter ausdrücken 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)) 

Amberlamps
quelle
3
Ja, ich würde meine Lösung für diese Art von Problem nicht empfehlen. Deshalb habe ich meine Antwort mit einer alternativen, besseren Lösung aktualisiert.
Amberlamps
1
Ich habe meine Antwort aktualisiert - es scheint (beim erneuten Lesen der Frage), dass das OP ein neues Objekt mit denselben Schlüsseln möchte , bei dem die Werte mit der angegebenen Funktion mutiert wurden, und das ursprüngliche Objekt nicht an Ort und Stelle zu ändern.
Alnitak
13
@KhalilRavanna Ich denke, Sie haben den Code hier falsch gelesen - diese Antwort wird nicht maprichtig verwendet, weil sie nicht funktioniert return- sie missbraucht, mapals wäre es ein forEachAnruf. Wenn er es tatsächlich tat, return myObject[value] * 2 wäre das Ergebnis ein Array, das die ursprünglichen Werte verdoppelt, anstelle eines Objekts, das die ursprünglichen Schlüssel mit doppelten Werten enthält, wobei letzteres eindeutig das ist, was das OP verlangt hat.
Alnitak
3
@Amberlamps Ihr .reduceBeispiel ist in Ordnung, aber meiner Meinung nach bleibt es weit hinter der Bequemlichkeit eines .mapfor Objects zurück, da Ihr .reduceRückruf nicht nur der Funktionsweise entsprechen muss .reduce, sondern auch erfordert, myObjectdass er im lexikalischen Bereich verfügbar ist. Insbesondere letzteres macht es unmöglich, nur eine Funktionsreferenz im Rückruf zu übergeben, und erfordert stattdessen eine direkte anonyme Funktion.
Alnitak
8
TBH Ich hätte lieber eine Antwort positiv bewertet, die nur (oder ursprünglich) die zweite in dieser Antwort vorgestellte Lösung lieferte. Der erste funktioniert und ist nicht unbedingt falsch, aber wie andere gesagt haben, mag ich es nicht, wenn die Karte so verwendet wird. Wenn ich Karte sehe, denkt mein Verstand automatisch "unveränderliche Datenstruktur"
mjohnsonengr
308

Wie wäre es mit einem Einzeiler mit sofortiger Variablenzuweisung in einfachem JS ( ES6 / ES2015 )?

Verwenden des Spread-Operators und der berechneten Schlüsselnamensyntax :

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

jsbin

Eine andere Version mit reduzieren:

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

jsbin

Erstes Beispiel als Funktion:

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

Wenn Sie ein verschachteltes Objekt rekursiv in einem funktionalen Stil zuordnen möchten , gehen Sie folgendermaßen vor:

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

Oder zwingender: so:

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

Seit ES7 / ES2016 können Sie Object.entries()statt Object.keys()zB folgendes verwenden:

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

ES2019 eingeführt Object.fromEntries(), was dies noch weiter vereinfacht:

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


Vererbte Eigenschaften und die Prototypkette:

In einigen seltenen Fällen , müssen Sie möglicherweise eine abzubilden Klasse ähnlichen Objekt , das Eigenschaften eines geerbten Objekt auf seiner hält Prototyp-Kette . In solchen Fällen Object.keys()funktioniert Object.keys()dies nicht , da geerbte Eigenschaften nicht aufgelistet werden. Wenn Sie geerbte Eigenschaften zuordnen müssen, sollten Sie verwenden for (key in myObj) {...}.

Hier ist ein Beispiel für ein Objekt, das die Eigenschaften eines anderen Objekts erbt und wie Object.keys()es in einem solchen Szenario nicht funktioniert.

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

Bitte tun Sie mir jedoch einen Gefallen und vermeiden Sie die Vererbung . :-)

Rotareti
quelle
1
Schön, aber ich wurde von der Tatsache Object.keysüberrascht , dass ererbte Eigenschaften nicht aufgezählt werden. Ich schlage vor, Sie fügen eine Warnung hinzu.
David Braun
Um Ihre Antwort zu verkürzen, verwenden Sie einfach Object.entries({a: 1, b: 2, c: 3}).
1
Sie haben einige Tippfehler (Sie bezeichnen (o, f)als Argumente, aber verwenden objim Körper.
Kzahel
4
Ja, und dann lassen Sie die nächste Person ein paar Minuten damit verbringen, herauszufinden, was diese Funktion bewirkt. :)
Andrey Popov
1
Die Lösung Object.assign(...Object.entries(obj).map(([k, v]) => ({[k]: v * v})))funktioniert nicht, wenn objes sich um ein leeres Objekt handelt. Wechseln zu : Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v}))).
Blackcatweb
116

Keine nativen Methoden, aber lodash # mapValues macht den Job hervorragend

_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
Mario
quelle
6
Es ist nicht erforderlich, einen zusätzlichen HTTP-Aufruf und eine zusätzliche Bibliothek nur für eine Funktion hinzuzufügen. In jedem Fall ist diese Antwort jetzt veraltet und Sie können einfach anrufen Object.entries({a: 1, b: 2, c: 3}), um ein Array zu erhalten.
11
Warum ist es nützlich, ein Array zu bekommen? Die Anforderung war für ein zugeordnetes Objekt. Es gibt auch keinen impliziten HTTP-Aufruf bei der Verwendung von Lodash, einer beliebten Bibliothek, die im Wesentlichen für diese Art von Dingen entwickelt wurde, obwohl ES6 in letzter Zeit die Notwendigkeit der Verwendung von Dienstprogrammfunktionsbibliotheken in gewissem Maße ausgeschlossen hat.
corse32
2
zusätzlicher HTTP-Aufruf, um Lodash zu ziehen, denke ich. Wenn dies Ihr einziger Anwendungsfall ist, ist es in der Tat ein Overkill. Trotzdem ist es sinnvoll, diese Antwort zu haben, denn Lodash ist eine so weit verbreitete Bibliothek.
igorsantos07
2
Ganz zu schweigen davon, dass Sie tatsächlich verwenden sollten, _.map()damit Sie den Schlüssel als zweites Argument erhalten - wie vom OP gefordert.
igorsantos07
_.mapValues()liefert auch den Schlüssel als 2. Argument. Auch _.map()würde hier nicht funktionieren, da es nur Arrays zurückgibt, keine Objekte:_.map({ 'a': 1, 'b': 2, 'c': 3}, n => n * 3) // [3, 6, 9]
MeltedPenguin
57

Es ist ziemlich einfach, eine zu schreiben:

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

mit Beispielcode:

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

Hinweis: In dieser Version können Sie (optional) den thisKontext für den Rückruf festlegen , genau wie bei der ArrayMethode.

BEARBEITEN - geändert, um die Verwendung von zu entfernen Object.prototype, um sicherzustellen, dass keine mit einer im Objekt genannten Eigenschaft kollidiert map.

Alnitak
quelle
3
OK. :) Ein Kommentar zu dieser Frage hat mich hierher gebracht. Ich werde dies +1 geben, da die akzeptierte Antwort eindeutig missbräuchlich ist map, aber ich muss sagen, dass das Ändern Object.prototypenicht gut zu mir passt, auch wenn es nicht aufzählbar ist.
JLRishe
2
@ JLRishe Danke. IMHO in ES5 gibt es wirklich keinen Grund mehr , Object.prototypeaußer dem (theoretischen) Kollisionsrisiko mit anderen Methoden nichts zu ändern . FWIW, ich kann nicht verstehen, wie die andere Antwort so viele Stimmen bekommen hat wie sie hat, es ist völlig falsch.
Alnitak
7
@JLRishe du bist ein alter Knacker ... geh mit der Zeit und modifiziere diesen Prototyp!
Nick Manning
12
@ NickManning Sie junge Whippersnapper und Ihr unverantwortliches Verhalten. Geh von meinem Rasen runter!
JLRishe
3
Das funktioniert schon nicht : o = {map: true, image: false}. Sie riskieren es nur zu schreiben o.map(fn)stattmap(o,fn)
fregante
25

Sie können Object.keysund dann forEachüber das zurückgegebene Array von Schlüsseln verwenden:

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

Oder modularer:

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

Beachten Sie, dass Object.keysein Array zurückgegeben wird, das nur die eigenen aufzählbaren Eigenschaften des Objekts enthält. Daher verhält es sich wie eine for..inSchleife mit einer hasOwnPropertyPrüfung.

Mattias Buelens
quelle
20

Dies ist gerade bs, und jeder in der JS-Community weiß es. Es sollte diese Funktionalität geben:

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}

Hier ist die naive Implementierung:

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

es ist super nervig, das die ganze zeit selbst umsetzen zu müssen;)

Wenn Sie etwas Anspruchsvolleres wünschen, das die Object-Klasse nicht beeinträchtigt, versuchen Sie Folgendes:

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

Es ist jedoch sicher, diese Zuordnungsfunktion zu Object hinzuzufügen. Fügen Sie sie nur nicht zu Object.prototype hinzu.

Object.map = ... // fairly safe
Object.prototype.map ... // not ok
Alexander Mills
quelle
Warum fügen Sie diese Funktion dem globalen ObjectObjekt hinzu? Einfach haben const mapObject = (obj, fn) => { [...]; return ret; }.
Amberlamps
7
Weil es eine Methode für Objekt sein sollte :)
Alexander Mills
1
Solange wir uns nicht mit Object.prototype anlegen, sollten wir in Ordnung sein
Alexander Mills
1
Wenn die Karte nur eine zufällige vage Sache wäre, sicher. Wird .mapaber allgemein als Functor-Schnittstelle verstanden, und es gibt keine einheitliche Definition von Functor für "Objekt", da praktisch alles in Javascript ein Objekt sein kann, einschließlich Dinge mit eigenen, sehr unterschiedlichen und definierten .map-Schnittstellen / Logik.
Dtipson
1
Meiner Meinung nach sollte das erste Argument des Rückrufs das iterierte Element selbst sein, kein Schlüssel. Zum Beispiel gehe ich davon aus, dass es mir dasselbe Objekt geben sollte, wie es normalerweise bei einer gemeinsamen Karte der Fall ist: Object.map ({prop: 1}, el => el), aber es gibt Schlüssel mit dem gleichen Schlüsselnamen zurück ...
Gleb Dolzikov
17

Ich bin hierher gekommen, um ein Objekt einem Array zuzuordnen und zu beantworten, und habe diese Seite als Ergebnis erhalten. Falls Sie hierher gekommen sind, um nach der gleichen Antwort zu suchen, die ich erhalten habe, können Sie hier ein Array zuordnen und Einwände erheben.

Sie können map verwenden, um ein neues Array vom Objekt wie folgt zurückzugeben:

var newObject = Object.keys(myObject).map(function(key) {
   return myObject[key];
});
JoeTron
quelle
6
Dies funktioniert nicht remote - es wird nur ein Array der Werte des Objekts zurückgegeben, kein neues Objekt. Ich kann nicht glauben, dass es so viele Stimmen gibt.
Alnitak
1
Sie haben Recht, die Frage wird nicht richtig beantwortet, da nach einer Antwort für die Zuordnung eines Objekts zu und eines Objekts gesucht wurde. Ich bin hierher gekommen, um nach einer Antwort für die Zuordnung eines Objekts zu einem Array zu suchen, und habe diese Seite als Ergebnis erhalten. Daher werde ich meine Antwort verlassen und sie bearbeiten, um zu reflektieren, dass es sich um eine alternative Antwort für Personen handelt, die möglicherweise aufgrund von hierher gekommen sind Suchen nach der Zuordnung eines Objekts zu Arrays.
JoeTron
5
In ES7 wird das trivial seinObject.values(myObject)
Alnitak
1
Das ist gut zu wissen, aber einige Leute müssen noch alten Code verwenden.
JoeTron
const obj = {foo: 'bar', baz: 42}; console.log (Object.entries (obj)); // [['foo', 'bar'], ['baz', 42]]
Bashirpour
12

Die akzeptierte Antwort hat zwei Nachteile:

  • Es missbraucht Array.prototype.reduce, weil Reduzieren Mittel zum Ändern der Struktur eines zusammengesetzten Typs bedeutet, was in diesem Fall nicht der Fall ist.
  • Es ist nicht besonders wiederverwendbar

Ein funktionaler Ansatz für ES6 / ES2015

Bitte beachten Sie, dass alle Funktionen in Curryform definiert sind.

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

  • In Zeile A owird neu zugewiesen. Da Javascript Referenzwerte durch Freigabe übergibt , wird eine flache Kopie von ogeneriert. Wir sind jetzt in der Lage, oinnerhalb zu mutieren, omapohne oim übergeordneten Bereich zu mutieren .
  • In Zeile B wird mapder Rückgabewert ignoriert, da mapeine Mutation von ausgeführt wird o. Da dieser Nebeneffekt omapim übergeordneten Bereich verbleibt und nicht sichtbar ist, ist er völlig akzeptabel.

Dies ist nicht die schnellste, sondern eine deklarative und wiederverwendbare Lösung. Hier ist die gleiche Implementierung wie bei einer einzeiligen, prägnanten, aber weniger lesbaren Implementierung:

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

Nachtrag - Warum sind Objekte standardmäßig nicht iterierbar?

ES2015 spezifizierte den Iterator und die iterierbaren Protokolle. Objekte sind jedoch immer noch nicht iterierbar und daher nicht abbildbar. Der Grund ist das Mischen von Daten und Programmebene .

Gemeinschaft
quelle
1
Sieht aus wie ein bisschen überarbeitet; Ich zähle 6 Funktionen für eine Schleife. Andere Lösungen sind viel einfacher und die akzeptierte Antwort ist auch fünfmal schneller.
Fregante
4
@ bfred.it Was ist der Zweck Ihres Kommentars? Wenn Sie Mikrooptimierung mögen, sollten Sie auch nicht verwenden Array.prototype.reduce. Ich möchte hier nur veranschaulichen, dass die akzeptierte Antwort irgendwie "missbraucht" Array.prototype.reduceund wie ein rein funktionales Mapping implementiert werden könnte. Wenn Sie nicht an funktionaler Programmierung interessiert sind, ignorieren Sie einfach meine Antwort.
2
"Reduzieren bedeutet, die Struktur eines zusammengesetzten Typs zu ändern" Ich glaube nicht, dass das stimmt. Ein Array.reduce, das ein neues Array mit genau derselben Länge oder sogar genau denselben Werten erzeugt, ist absolut legitim. Reduzieren kann verwendet werden, um die Funktionalität von Karte und / oder Filter neu zu erstellen (oder beides, wie bei einem komplexen Wandler, für den Reduzieren die Basis ist). In FP ist es eine Art von Falte, und eine Falte, die mit derselben Art endet, ist immer noch eine Falte.
Dtipson
2
@Dtipson Du hast absolut Recht. A foldist allgemeiner als a map(Funktor). Dies war jedoch mein Wissen ab Mitte 2016. Das ist eine halbe Ewigkeit: D
1
Ha, ja. Ich habe Jahre gebraucht, um zu erkennen, wie miteinander verbunden all diese Dinge und das breitere Ökosystem von Begriffen und Schnittstellen sind. Dinge, die so einfach zu sein scheinen, erweisen sich als unglaublich komplex, und einige Dinge, die unglaublich komplex erscheinen, lassen sich recht ordentlich abstrahieren. :)
Dtipson
10

JavaScript hat gerade die neue Object.fromEntriesMethode bekommen.

Beispiel

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)

Erläuterung

Der obige Code konvertiert das Objekt in ein verschachteltes Array ( [[<key>,<value>], ...]), das Sie zuordnen können. Object.fromEntrieskonvertiert das Array zurück in ein Objekt.

Das Coole an diesem Muster ist, dass Sie jetzt beim Zuordnen problemlos Objektschlüssel berücksichtigen können.

Dokumentation

Browser-Unterstützung

Object.fromEntrieswird derzeit nur von diesen Browsern / Engines unterstützt , es sind jedoch Polyfills verfügbar (zB @ babel / polyfill ).

HaNdTriX
quelle
2
Grundsätzlich Object.fromEntriesist das Gegenteil von Object.entries. Object.entrieskonvertiert ein Objekt in eine Liste von Schlüssel-Wert-Paaren. Object.fromEntrieskonvertiert eine Liste von Schlüssel-Wert-Paaren in ein Objekt.
Orad
8

Minimale Version (es6):

Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
Yukulélé
quelle
Ich denke, das ist wegen eines Tippfehlers falsch. Ich denke, Sie meinen => Object.entries(obj).reduce((a, [k, v]) => [a[k] = v * v, a], {}), spitze Klammern anstelle von Klammern.
Alexander Mills
@ AlexanderMills, mein Code ist korrekt. Lesen Sie mehr über Komma-Operator in Klammern: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Yukulélé
verstanden, vielleicht möchten Sie das nächste Mal erklären, aber ich denke, dafür sind Kommentare gedacht.
Alexander Mills
1
Im Grunde ist mein Code äquivalent zu:Object.entries(obj).reduce((a, [k, v]) => {a[k] = v * v; return a}, {})
Yukulélé
1
Gute Antwort. Object.entries().reduceist die beste Lösung, die ich mit ES6 + denke. Würde mehr Upvotes bekommen, wenn es eine returnAussage im Reduzierer anstelle von implicit returnund commaOperator verwenden würde - was ordentlich, aber
Drenai
7

Für maximale Leistung.

Wenn sich Ihr Objekt nicht oft ändert, aber häufig wiederholt werden muss, empfehle ich die Verwendung einer nativen Karte als Cache.

// 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 funktioniert bereits in Chrome, Edge, Firefox und Beta Opera und ist daher zukunftssicher. Es ist von ES7, also füllen Sie es mit https://github.com/es-shims/Object.entries für IE, wo es nicht funktioniert.

Pawel
quelle
Wie wäre es mit Destructing ?
Константин Ван
1
@ K._ es war früher sehr langsam, aber ich weiß jetzt nichts über die Leistung. Es sieht so aus, als ob V8 v 5.6 einige Optimierungen für die Destrukturierung eingeführt hat, aber das muss gemessen werden v8project.blogspot.co.uk
Pawel
Oder weniger performant es7 aber unveränderlich:const fn = v => v * 2; const newObj = Object.entries(myObject).reduce((acc, [k,v]) => Object.assign({}, acc, {[k]: fn(v)}), {});
Mikebridge
Ich frage mich, warum keine der anderen Antworten Object.entries verwendet. Es ist so viel sauberer als über Schlüssel zu iterieren und obj [Schlüssel] zu verwenden.
Corwin.amber
6

Sie können die mapMethode und forEachArrays verwenden, aber wenn Sie sie verwenden möchten, Objectkönnen Sie sie mit dem folgenden Twist verwenden:

Verwenden von 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}

Verwenden von 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}

Oder Sie können andere Schleifen wie die folgende $.eachMethode verwenden: Beispiel:

$.each(ob,function (key, value) {
  ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}
Haritsinh Gohil
quelle
Wo $ist jquery?
Masterxilo
Ja, Sie können sowohl $ als auch jQuery
Haritsinh Gohil
1
@Ronald Ich habe es falsch verstanden, siehe meine aktualisierte Antwort, danke, dass du nicht herabgestimmt hast, aber wirklich auf meinen Fehler aufmerksam gemacht hast, danke, dass du die Community verbessert hast.
Haritsinh Gohil
@bcoughlan ja es ist genau das, was ich auch im Kommentar geschrieben habe, es quadriert die Zahlen, was willst du dann?
Haritsinh Gohil
1
@ HaritsinhGohil Ich habe es so verstanden, dass er Sie dafür kritisiert hat, dass Sie eine jQuery-Lösung gegeben haben. Es ist wahr, dass es heutzutage einige Impulse gibt, sich von jQuery zu entfernen, und natürlich gibt es Node, aber wenn ein OP nicht speziell nach reinem JS oder Node fragt, ist es in Ordnung anzunehmen, dass jemand in einer Webumgebung und in jQuery arbeitet ist fast sicher verfügbar zu sein.
Abalter
4

Das map functiongibt es auf dem nicht, Object.prototypeaber Sie können es so emulieren

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;
});
Whitneyit
quelle
Leistungsprobleme: typeof callback === 'function'absolut überflüssig. 1) maphat ohne Rückruf keine Bedeutung 2) Wenn dies callbacknicht funktioniert, wird Ihre mapImplementierung nur ohne Bedeutung für die for-Schleife ausgeführt. Auch Object.prototype.hasOwnProperty.call( obj, key )ist ein bisschen übertrieben.
Ankhzet
3

Ich bin darauf als erstes Element in einer Google-Suche gestoßen, als ich versuchte, dies zu lernen, und dachte, ich würde es anderen Leuten mitteilen, die kürzlich die Lösung gefunden haben, die das unveränderliche npm-Paket verwendet.

Ich denke, es ist interessant zu teilen, da unveränderlich die EXAKTE Situation des OP in seiner eigenen Dokumentation verwendet - das Folgende ist nicht mein eigener Code, sondern stammt aus der aktuellen Dokumentation von unveränderlichem 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 } 

Nicht, dass Seq andere Eigenschaften hat ("Seq beschreibt eine verzögerte Operation, die es ihnen ermöglicht, die Verwendung aller Erfassungsmethoden höherer Ordnung (wie Map und Filter) effizient zu verketten, indem keine Zwischensammlungen erstellt werden") und einige andere unveränderliche js-Daten Strukturen könnten die Arbeit auch recht effizient erledigen.

Jeder, der diese Methode verwendet, muss npm install immutableund möchte möglicherweise die folgenden Dokumente lesen:

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

alex.h
quelle
3

BEARBEITEN: Der kanonische Weg mit neueren JavaScript-Funktionen ist -

const identity = x =>
  x

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

Wo oist ein Objekt und fist Ihre Mapping-Funktion. Oder wir könnten sagen, wenn eine Funktion von a -> bund ein Objekt mit Werten vom Typ gegeben sind a, erzeugen wir ein Objekt mit Werten vom Typ b. Als Pseudotyp-Signatur -

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

Die ursprüngliche Antwort wurde geschrieben, um einen leistungsstarken Kombinator zu demonstrieren, mapReduceder es uns ermöglicht, unsere Transformation auf andere Weise zu betrachten

  1. m, die Zuordnungsfunktion - gibt Ihnen die Möglichkeit, das eingehende Element zu transformieren, bevor…
  2. r, die Reduktionsfunktion - Diese Funktion kombiniert den Akkumulator mit dem Ergebnis des abgebildeten Elements

Erstellt intuitiv mapReduceeinen neuen Reduzierer, an den wir direkt anschließen können Array.prototype.reduce. Noch wichtiger ist jedoch, dass wir unsere Objektfunktor-Implementierung omapeinfach implementieren können, indem wir das Objektmonoid Object.assignund verwenden {}.

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 }

Beachten Sie, dass der einzige Teil des Programms, den wir tatsächlich schreiben mussten, die Mapping-Implementierung selbst ist -

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

Das heißt, wenn ein bekanntes Objekt ound ein Schlüssel gegeben sind k, konstruieren Sie ein Objekt, dessen berechnete Eigenschaft kdas Ergebnis des Aufrufs fdes Schlüsselwerts ist o[k].

Wir bekommen einen Einblick in mapReducedas Sequenzierungspotential, wenn wir zuerst abstrahierenoreduce

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

Alles funktioniert gleich, omapkann aber jetzt auf einer höheren Ebene definiert werden. Natürlich Object.entriesmacht das Neue das albern, aber die Übung ist für den Lernenden immer noch wichtig.

Sie werden hier nicht das volle Potenzial mapReducesehen, aber ich teile diese Antwort, weil es interessant ist zu sehen, an wie vielen Stellen sie angewendet werden kann. Wenn Sie daran interessiert sind, wie es abgeleitet wird und auf welche Weise es nützlich sein könnte, lesen Sie bitte diese Antwort .

Vielen Dank
quelle
4
Verwenden wir lieber Mapund Map.entries, OK: D. Ich bin es leid, einfache Objekte als Kartendatentypen zu missbrauchen.
2
Ich bin damit einverstanden, Mapsollte verwendet werden, wo / wenn möglich, aber es ersetzt nicht vollständig die Notwendigkeit, diese Verfahren für einfache JS-Objekte zu verwenden, obwohl: D
Danke
2

Basierend auf der Antwort von @Amberlamps ist hier eine Utility-Funktion (als Kommentar sah sie hässlich aus)

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

und die Verwendung ist:

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}
yonatanmn
quelle
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 }
Dmitry Ragozin
quelle
enumerableStandardmäßigfalse
Danke
2

Meine Antwort basiert größtenteils auf der am höchsten bewerteten Antwort hier und hoffentlich versteht jeder (habe die gleiche Erklärung auch auf meinem GitHub). Deshalb funktioniert seine Implementierung mit der Karte:

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

Der Zweck der Funktion besteht darin, ein Objekt zu nehmen und den ursprünglichen Inhalt des Objekts mithilfe einer Methode zu ändern, die allen Objekten (Objekten und Arrays gleichermaßen) zur Verfügung steht, ohne ein Array zurückzugeben. Fast alles in JS ist ein Objekt, und aus diesem Grund können Elemente weiter unten in der Vererbungspipeline möglicherweise technisch diejenigen verwenden, die für diejenigen auf der ganzen Linie verfügbar sind (und umgekehrt, wie es scheint).

Der Grund dafür ist, dass die .map-Funktionen ein Array zurückgeben, das erfordert, dass Sie eine explizite oder implizite RETURN eines Arrays angeben, anstatt einfach ein vorhandenes Objekt zu ändern. Sie täuschen das Programm im Wesentlichen vor, das Objekt sei ein Array, indem Sie Object.keys verwenden, mit dem Sie die Map-Funktion verwenden können, die auf die Werte einwirkt, denen die einzelnen Schlüssel zugeordnet sind (ich habe Arrays tatsächlich versehentlich zurückgegeben, aber behoben). Solange es keine Rückkehr im normalen Sinne gibt, wird kein Array erstellt, bei dem das ursprüngliche Objekt noch intakt ist und wie programmiert geändert wird.

Dieses spezielle Programm nimmt ein Objekt namens images und nimmt die Werte seiner Schlüssel und hängt URL-Tags zur Verwendung in einer anderen Funktion an. Original ist das:

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' };

... und modifiziert ist dies:

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

Die ursprüngliche Struktur des Objekts bleibt erhalten, sodass ein normaler Zugriff auf die Eigenschaft möglich ist, solange keine Rückgabe erfolgt. Lassen Sie es NICHT wie gewohnt ein Array zurückgeben, und alles wird gut. Das Ziel ist es, die ursprünglichen Werte (Bilder [Schlüssel]) dem zuzuordnen, was gewünscht wird, und nicht irgendetwas anderem. Soweit ich weiß, MUSS eine Neuzuordnung von Bildern [Schlüssel] und keine implizite oder explizite Aufforderung zur Rückgabe eines Arrays erfolgen, um eine Array-Ausgabe zu verhindern (die Variablenzuweisung tut dies und war für mich ein Fehler).

BEARBEITEN:

Gehen Sie auf seine andere Methode zur Erstellung neuer Objekte ein, um zu vermeiden, dass das ursprüngliche Objekt geändert wird (und eine Neuzuweisung scheint weiterhin erforderlich zu sein, um zu vermeiden, dass versehentlich ein Array als Ausgabe erstellt wird). Diese Funktionen verwenden die Pfeilsyntax und sind, wenn Sie einfach ein neues Objekt für die zukünftige Verwendung erstellen möchten.

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

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

Die Funktionsweise dieser Funktionen ist wie folgt:

mapFn übernimmt die Funktion, die später hinzugefügt wird (in diesem Fall (Wert) => Wert) und gibt einfach alles, was dort gespeichert ist, als Wert für diesen Schlüssel zurück (oder multipliziert mit zwei, wenn Sie den Rückgabewert wie er ändern) in mapFn ( obj) [Schlüssel],

und definiert dann den ursprünglichen Wert neu, der dem Schlüssel in result [key] = mapFn (obj) [key] zugeordnet ist.

und gibt die Operation zurück, die für das Ergebnis ausgeführt wurde (der Akkumulator in den Klammern, die am Ende der .reduce-Funktion ausgelöst wurden).

All dies wird für das ausgewählte Objekt ausgeführt und es kann NOCH KEINE implizite Anforderung für ein zurückgegebenes Array geben und funktioniert nur, wenn Werte neu zugewiesen werden, soweit ich das beurteilen kann. Dies erfordert etwas mentale Gymnastik, reduziert jedoch die benötigten Codezeilen, wie oben zu sehen ist. Die Ausgabe ist genau die gleiche wie unten gezeigt:

{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"}

Denken Sie daran, dass dies mit NON-NUMBERS funktioniert hat. Sie können jedes Objekt duplizieren, indem Sie einfach den Wert in der Funktion mapFN zurückgeben.

Andrew Ramshaw
quelle
2

Die erste Funktion beantwortet die Frage. Es wird eine Karte erstellt.

Die zweite Funktion erstellt keine Karte. Stattdessen werden Eigenschaften im vorhandenen Objekt mit durchlaufen hasOwnProperty(). Diese Kartenalternative kann in einigen Situationen eine bessere Lösung sein.

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>

Victor Stoddard
quelle
2
Dies beantwortet nicht die ursprüngliche Frage - es ordnet die Werte nicht einem neuen Objekt mit einer bereitgestellten Funktion zu (dh dies ist nichts anderes als Array.prototype.map).
Matt Browne
@MattBrowne Ich habe eine zweite Methode hinzugefügt, mit der eine Karte erstellt wird. Es ist langsam, macht aber das, was das OP verlangt. Die erste Methode ist in vielen Situationen ideal, weshalb ich eine Schleife und Eigenschaftsfunktionen verwendet habe.
Victor Stoddard
1

Wenn Sie mapnicht nur Werte, sondern auch Schlüssel anpingen möchten, habe ich geschrieben Object.map(valueMapper, keyMapper), dass sich das so verhält:

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 }
MattiSG
quelle
3
Ich nehme an, weil Sie im Grunde nur einen Link als Antwort angegeben haben, der verpönt ist.
Chris Wright
Wenn jemals für jemanden nützlich : npm install @mattisg/object.map.
MattiSG
1

Ich brauchte eine Version, mit der auch die Schlüssel geändert werden konnten (basierend auf den Antworten von @Amberlamps und @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"}
}

Bearbeiten: leichte Änderung, um das Startobjekt {} zu übergeben. Ermöglicht es, [] zu sein (wenn die Schlüssel Ganzzahlen sind)

Symmetrisch
quelle
1

Ich wollte speziell die gleiche Funktion verwenden, die ich für Arrays für ein einzelnes Objekt verwendet habe, und wollte es einfach halten. Das hat bei mir funktioniert:

var mapped = [item].map(myMapFunction).pop();
Jason Norwood-Young
quelle
das ist nicht anders alsvar mapped = myMapFunction(item)
Danke
Dies ist so viel einfacher als alle anderen "Rocket Science" -Lösungen, die ich hier gesehen habe. Danke fürs Teilen :)
Yuri Teixeira
1

Um reagiert stärker auf , was genau die OP gefragt, will das OP ein Objekt:

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

eine Kartenmethode haben myObject.map,

ähnlich wie Array.prototype.map, das wie folgt verwendet wird:

newObject = myObject.map (Funktion (Wert, Bezeichnung) {
    Rückgabewert * Wert;
});
// newObject ist jetzt {'a': 1, 'b': 4, 'c': 9}

Die imho beste Antwort (gemessen an " nahe an dem, was gefragt wird " + "kein ES {5,6,7} unnötig erforderlich") wäre:

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

Der obige Code vermeidet die absichtliche Verwendung von Sprachfunktionen, die nur in neueren ECMAScript-Editionen verfügbar sind. Mit dem obigen Code kann das Problem folgendermaßen gelöst werden:

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);
alternativer Testcode hier

Abgesehen davon, dass einige die Stirn runzeln, wäre es eine Möglichkeit, die Lösung so in die Prototypenkette einzufügen.

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

Etwas, das, wenn es sorgfältig überwacht wird, keine negativen Auswirkungen haben und die mapMethode anderer Objekte (z. B. Arrays map) nicht beeinflussen sollte.

Menschlichkeit UND Frieden
quelle
1

Definieren Sie eine Funktion mapEntries.

mapEntries verwendet eine Rückruffunktion, die für jeden Eintrag im Objekt mit den Parametern value, key und object aufgerufen wird. Es sollte einen neuen Wert zurückgeben.

mapEntries sollte ein neues Objekt mit den neuen Werten zurückgeben, die vom Rückruf zurückgegeben wurden.

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}

Bearbeiten: In einer früheren Version wurde nicht angegeben, dass dies keine aufzählbare Eigenschaft ist

Arye Eidelman
quelle
0

Hey hat eine kleine Mapper-Funktion geschrieben, die helfen könnte.

    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];
                }
              }
            }
         }
      }
Zak
quelle
1
Ich denke du meintest "ich" statt "Hey"
Carlo
0

Eine andere Sichtweise ist die Verwendung einer benutzerdefinierten JSON-Stringify-Funktion, die auch für tiefe Objekte geeignet ist. Dies kann nützlich sein, wenn Sie es trotzdem als json auf dem Server veröffentlichen möchten

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

Endlos
quelle
0

Ich behandle nur Zeichenfolgen, um Ausnahmen zu reduzieren:

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

Object Mapper in TypeScript

Ich mag die Beispiele, die Object.fromEntrieswie dieses verwendet werden , aber dennoch sind sie nicht sehr einfach zu verwenden. Die Antworten, die die verwenden Object.keysund dann nachschlagen, keyführen tatsächlich mehrere Suchvorgänge durch, die möglicherweise nicht erforderlich sind.

Ich wünschte, es gäbe eine Object.mapFunktion, aber wir können unsere eigene erstellen und sie objectMapmit der Fähigkeit aufrufen , beide zu ändern keyund value:

Verwendung (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' }

Code (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;
}

Wenn Sie TypeScript nicht verwenden, kopieren Sie den obigen Code in TypeScript Playground , um den JavaScript-Code zu erhalten.

Auch der Grund Ich habe keySelectornach valSelectorder Parameterliste, ist , weil es optional.

* Ein Teil des Verdienstes geht an die Antwort von Alexander Mills .

orad
quelle
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}]}]}

Diese Funktion geht rekursiv durch das Objekt und die Arrays von Objekten. Attribute können gelöscht werden, wenn sie undefiniert zurückgegeben werden

Stanislav
quelle