Konvertieren Sie das Objektarray mithilfe von lodash in eine Hashkarte

137173
Pete

Der Anwendungsfall besteht darin, ein Array von Objekten in eine Hash-Map zu konvertieren, wobei eine Eigenschaft der Schlüssel und die andere Eigenschaft der Wert ist. Im Normalfall wird ein "Link" -Objekt in einer Hypermedia-Antwort in eine Hash-Map von Links konvertiert.

jsfiddle

function toHashMap(data, name, value) {
    return _.zipObject(_.pluck(data, name),
                       _.pluck(data, value));
}

function toMap(data, name, value) {
    return _.reduce(data, function(acc, item) {
        acc[item[name]] = item[value];
        return acc;
    }, {});
}

var data = [
    { rel: 'link1', href: 'url1'},
    { rel: 'link2', href: 'url2'},
    { rel: 'link3', href: 'url3'},
    { rel: 'link4', href: 'url4'},
];

console.log(toHashMap(data, 'rel', 'href'));

console.log(toMap(data, 'rel', 'href'));

toHashMaperscheint viel lesbarer, aber weniger effizient. toMapscheint die beste Effizienz auf Kosten der Lesbarkeit zu haben.

Gibt es eine elegantere Lösung - dh lesbar und effizient? Ich vermute, dass Lodash bereits eine Funktion hat, um dies zu tun, aber ich habe nichts dergleichen gefunden, nachdem ich mir die API-Dokumentation angesehen hatte.

Antworten
69
Ich habe die gebräuchlichsten Methoden zusammengestellt, um ein Objekt in [diese Anforderung der lodash-Funktion] (https://github.com/lodash/lodash/issues/2718) _ in ein Array umzuwandeln (was aktuell Upvotes benötigt!) _ BlueRaja - Danny Pflughoeft vor 3 Jahren 0

7 Antworten auf die Frage

86
Jon Anderson

Ich denke du suchst _.keyBy(oder _.indexByin älteren Versionen)

_.keyBy(data, 'rel');
Ich verstehe nicht, warum dies die akzeptierte Antwort ist, da sie nicht das gleiche Ergebnis liefert wie die exponierten Funktionen Pau Fracés vor 5 Jahren 3
@ PauFracés Siehe unten für eine überarbeitete Lösung. Pete vor 5 Jahren 1
Hinweis: Jetzt auch bekannt als `_.keyBy` (https://lodash.com/docs#keyBy). jtheletter vor 4 Jahren 4
40
Pete

Überarbeitete Lösung

Wie in dem obigen Kommentar von Pau Fracés beschrieben, ist hier die vollständige Lösung. Die von John Anderson gegebene Lösung würde alle Objekte anhand des Schlüssels indizieren. Dadurch würde jedoch keine Zuordnung von Schlüsselwerten erstellt.

Um die Lösung zum Erstellen einer vollständigen Hash-Map abzuschließen, müssen die Werte dem Schlüssel zugeordnet werden. Mit der Funktion mapValues können die Werte aus den Objekten extrahiert und auf den Schlüssel oder in diesem Fall abgebildet werden rel.

Pseudo-Code

  1. Indizieren Sie alle Objekte mit dem ausgewählten Schlüssel.
  2. Ordnen Sie alle Werte dem Schlüssel zu.

Code

Unten ist der vollständige Code mit aktivierter Protokollierung. Entfernen Sie bei einer nicht protokollierenden Version alle Zeilen mit der tapFunktion.

var data = [{ rel: 'link1', href: 'url1' }, 
            { rel: 'link2', href: 'url2' }, 
            { rel: 'link3', href: 'url3' },
            { rel: 'link4', href: 'url4' }];

function log(value) {
  document.getElementById("output").innerHTML += JSON.stringify(value, null, 2) + "\n"
}

var hashmap = _.chain(data)
  .keyBy('rel')

  .tap(log) // Line used just for logging

  .mapValues('href')

  .tap(log)
  
  .value();
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.min.js"></script>
<pre id="output"></pre>

Schöne Lösung! Persönlich würde ich "mapValues" anstelle von "transform" wählen. Mit "transform" aktualisieren Sie den Status außerhalb der Schließung (dies ist eine Nebeneffektfunktion), während Sie mit "mapValues" dies nicht tun. Martijn vor 5 Jahren 1
@Martijn Oh, toller Vorschlag! Bietet eine viel sauberere Lösung IMHO. Ich habe die Lösung mit Ihrem Vorschlag aktualisiert. Vielen Dank! Pete vor 5 Jahren 1
@Pete Awesome! Ich habe gerade realisiert, dass lodash v4 `indexBy` in` keyBy` umbenannt hat Pau Fracés vor 4 Jahren 1
Aktualisiert, um "keyBy" zu verwenden Pete vor 4 Jahren 0
Mit lodash-fp kann dies auch als One-Liner-Flow durchgeführt werden (fpKeyBy ('rel'), fpMapValues ​​('href')) (data) `(fp-Präfix-Funktionen sind fp-lodash-Versionen) Spain Train vor 4 Jahren 1
16
hon2a

In ES7 ist es jetzt so einfach wie:

data.reduce((acc, { rel, href }) => ({ ...acc, [rel]: href }), {});

(ohne auch nur Lodash zu benutzen).

wäre toll wenn es funktioniert. aber es scheint nicht zu funktionieren! (Knoten 5.0.0) Rene Wooller vor 4 Jahren 0
Dies ist ES7, nicht ES2015 Rene Wooller vor 4 Jahren 0
@ReneWooller Richtig, Entschuldigung. Schwer zu verfolgen, was bei der Verwendung von Babel ist :) hon2a vor 4 Jahren 0
Das funktioniert und es ist schön, nicht die lodash-Abhängigkeit zu haben, aber verdammt, wenn der lodash-Weg nicht eine Million Mal lesbarer ist. Thor84no vor 2 Jahren 0
@ Thor84no Full Lodash Lösung ist zB `_.mapValues ​​(_. KeyBy (data, 'rel'), 'href')` (die akzeptierte Antwort liefert nicht das, was das OP verlangt hat). Die Lesbarkeit hängt jedoch von Ihrer Vertrautheit mit Lodash ab. hon2a vor 2 Jahren 0
Es gibt mehr lesbare Schreibweisen als das. Die andere Antwort zum Beispiel ist wirklich klar: https://codereview.stackexchange.com/a/84028/84979 Thor84no vor 2 Jahren 0
Ob Sie verketten oder komponieren, ist unerheblich. Sie vergleichen Äpfel und Orangen. Ja, wenn Sie Lodash bereits verwenden und mit diesen Helfern vertraut sind, ist die Lösung, die sie verwendet, besser lesbar. Mein Ziel war es nur zu zeigen, dass es auch ganz einfach ist, wenn Sie es nicht tun. hon2a vor 2 Jahren 0
9
Matt

Ein einfacherer Weg wäre die Verwendung von "Reduzieren".

var data = [
    { rel: 'link1', href: 'url1'},
    { rel: 'link2', href: 'url2'},
    { rel: 'link3', href: 'url3'},
    { rel: 'link4', href: 'url4'},
];

var hashmap = _.reduce(data, function(hash, value) {
  var key = value['rel'];
  hash[key] = value['href'];
  return hash;
}, {});

JSFiddle: https://jsfiddle.net/6txzzxq2/

Mit verkleinern können Sie das Quell-Array mit einem "Akku" (in diesem Fall einem neuen Objekt) durchlaufen. Oben geben wir den Hash durch das Attribut rel mit href als Wert ein.

Sie können auch `.transform` verwenden, um die Menge an Code zu reduzieren ... aber das ist eine Frage der persönlichen Vorlieben. Pete vor 4 Jahren 0
Wie bereits von Martijn in einem früheren Kommentar festgestellt, werden sowohl '.reduce' als auch '.transform' außerhalb der Schließung aktualisiert, wohingegen 'mapValues' dies nicht tut. Beachten Sie dies bei der Verwendung von ".reduce". Pete vor 4 Jahren 0
@Pete Eine Aktualisierung außerhalb des Status ist keine inhärente Eigenschaft von '.reduce', sondern des Reduzierers selbst. Sie können '.reduce' genauso gut ohne Nebenwirkungen verwenden (siehe meine kurze Antwort). hon2a vor 4 Jahren 0
@Hon2a Richtig ... Ich bin bei dieser speziellen "Reduzierungs" -Lösung etwas Ungewöhnliches, obwohl "rel" und "href" im Callback fest codiert sind. Es macht es weniger wiederverwendbar. Ich habe versucht, das in der ursprünglichen Lösung zu vermeiden. Die Unterhaltskosten von einem Ein-Liner zu einem Fünf-Liner sind für mich viel attraktiver. Im Hinblick auf die Lesbarkeit ist "verkleinern" weniger fließend als "keyBy" und "mapValues" mit einem Nichtprogrammierer. Zugegeben, dies alles geht zu Lasten der Leistung und somit sind nicht alle Lösungen am Ende. Vielen Dank für die Aktualisierung auf ES2015. Pete vor 4 Jahren 0
@Pete Ich würde behaupten, dass eine einfache Verwendung von "verkleinern" tatsächlich besser lesbar ist als "keyBy" + "mapValues ​​(propNameShortcut)", da "verkleinern" eine native (also allgegenwärtige) Methode ist, die Sie nicht kennen müssen die Details einer bestimmten (Lodash) API. hon2a vor 4 Jahren 0
@ hon2a Es ist eine Frage der persönlichen Vorlieben, aber für diesen speziellen Fall wurde nach einer Lösung mit lodash gesucht. Die Annahme ist also, dass der Entwickler die lodash-API in ihrem Projekt verwendet. Es erinnerte mich an ein Projekt, bei dem der Architekt gesagt hatte, keine Frameworks zu verwenden, da Entwickler die APIs für das Framework möglicherweise nicht kennen. In einigen Fällen kann das Programmieren mit Frameworks mit alternativen APIs hilfreich sein und die Wartung und Lesbarkeit erleichtern. Es kann argumentiert werden, dass lodash eine dieser Bibliotheken ist, aber wiederum ist es eine persönliche Präferenz. Pete vor 4 Jahren 0
@Pete Sicher, das ist sehr subjektiv, ich wollte nur diesen Punkt einschließen :) Für mich persönlich machen native vs. library einen Unterschied in der Lesbarkeit, obwohl ich Lodash stark verwende. hon2a vor 4 Jahren 0
6
Ricardo Stuven

Seit lodash 4 können Sie _.fromPairs verwenden :

var data = [
    { rel: 'link1', href: 'url1'},
    { rel: 'link2', href: 'url2'},
    { rel: 'link3', href: 'url3'},
    { rel: 'link4', href: 'url4'},
];

var hashmap = _.fromPairs(data.map(function(item) {
   return [item.rel, item.href];
}));
Gibt es eine Möglichkeit, 'rel' und 'href' außerhalb der Methode 'map' zu parametrisieren, um die Lösung generischer zu gestalten? Pete vor 4 Jahren 1
Funktion makePair (keyProperty, valueProperty) {return-Funktion (item) {return [item [keyProperty], item [valueProperty]]; }; } var hashmap = _.fromPairs (data.map (makePair ('rel', 'href'))); Ricardo Stuven vor 3 Jahren 1
2
rocketspacer

Mit Lodash 4:

Mit keyByundmapValues

_.mapValues(_.keyBy(data, 'rel'), v => v.href);

Oder:

_.chain(data).keyBy('rel').mapValues(v => v.href).value();
0
Abhishek Ghosh

Ich sehe schon viele gute Antworten. Ich möchte nur noch eine weitere Antwort zur Liste hinzufügen, indem Sie Ramdas indexBy verwenden .

const data = [{
    id: 1,
    foo: 'bar'
  },
  {
    id: 2,
    baz: 'lorem'
  }
];

const dataById = R.indexBy(R.prop('id'), data);
console.log(dataById)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Ich hoffe es hilft jemand anderem :)

Dies ist nicht wirklich eine Überprüfung des bereitgestellten Codes, oder? Mast vor 2 Jahren 0
Leider wurde die Nutzung dieser Website missverstanden. Mehr mit StackOverflow vertraut :) Abhishek Ghosh vor 2 Jahren 0