Bei einem verschachtelten Objekt wie diesem:

var cars = {
    "bentley": {
        "suppliers": [
            {
            "location": "England",
            "name": "Sheffield Mines"}
        ]
        // ...
    }
};

Und in einem Array wie diesem ["bentley", "suppliers", "0", "name"] gibt es eine vorhandene Funktion, die das tiefste Element, d. h. pluck_innards(cars, ['bentley', "suppliers", "0", "name"]), zupft und "Sheffield Mines" zurückgibt.

Mit anderen Worten, gibt es eine Funktion (die ich deep_pluck nennen werde) wo

deep_pluck(cars, ['bentley', 'suppliers', '0', 'name']) 
         === cars['bentley']['suppliers']['0']['name']

Es scheint mir, dass dies einfach und doch häufig genug ist, um wahrscheinlich in einer der Javascript-Dienstprogrammbibliotheken wie jQuery oder lo-dash / unterstreichen - aber ich habe es nicht gesehen.

Mein Gedanke ist etwas Triviales in der Art von:

function deep_pluck(array, identities) {
    var this_id = identities.shift();
    if (identities.length > 0) {
        return deep_pluck(array[this_id], identities);
    }
    return array[this_id];
}

Was ich auf jsFiddle gepostet habe.

Es wäre natürlich hilfreich, wenn die Funktion intelligent genug wäre, um zu identifizieren, wann numerische Indizes in Arrays benötigt werden. Ich bin mir nicht sicher, welche anderen Vorbehalte ein Problem darstellen könnten.

Dies ist alles eine ziemlich lange Frage für etwas, von dem ich mir vorstelle, dass es bereits geschickt gelöst wurde, aber ich dachte, dies zu posten, da ich daran interessiert wäre, zu sehen, welche Lösungen es gibt.

8
Brian M. Hunt 8 Okt. 2012 im 20:07

4 Antworten

Beste Antwort

Ich glaube nicht, dass Sie Probleme mit Array-Indizes haben werden, wenn Sie sie als Nummer 0 übergeben.

Hier ist eine alternative Version Ihrer Funktion ohne Rekursion:

function deep_pluck(object, identities) {
    var result = object;
    for(var i = 0; i < identities.length; i++) {
        result = result[identities[i]];
    }
    return result;
}

Arbeitsbeispiel hier: http://jsfiddle.net/AmH2w/1/

2
Inferpse 8 Okt. 2012 im 16:20

Obwohl es sich nicht um eine generische Bibliothek handelt, scheint CasperJS mit seinem utils.getPropertyPath Funktion.

/**
 * Retrieves the value of an Object foreign property using a dot-separated
 * path string.
 *
 * Beware, this function doesn't handle object key names containing a dot.
 *
 * @param  Object  obj   The source object
 * @param  String  path  Dot separated path, eg. "x.y.z"
 */
function getPropertyPath(obj, path) {
    if (!isObject(obj) || !isString(path)) {
        return undefined;
    }
    var value = obj;
    path.split('.').forEach(function(property) {
        if (typeof value === "object" && property in value) {
            value = value[property];
        } else {
            value = undefined;
        }
    });
    return value;
}

Bearbeiten:

Ich bin seitdem ein paar Mal auf Implementierungen gestoßen, um dies zu lösen, einschließlich:

  1. das getObject -Plugin von Ben Alman (auf Github).
  2. eine, die ich gewürfelt habe - siehe Kern

Bearbeiten (2014)

Ich würde auch die relativ neue lodash.deep beachten.

1
Brian M. Hunt 21 Feb. 2014 im 19:56

Dotty.get (obj, pathspec) führt dies aus und akzeptiert entweder ein Array oder eine gepunktete Zeichenfolge als Pfadspezifikation.

Dotty ist Open Source und verfügt über eine vorhandene Methode und einen Putter.

Die Methode ist rekursiv und Ihrer Idee sehr ähnlich, außer dass dotty einen Test für null / undefinierte Objekte enthält, damit keine Ausnahmen für den Versuch ausgelöst werden, auf ein Element von etwas zuzugreifen, das nicht existiert.

Die dotty.get () -Quelle aus den Dokumenten ist unten aufgeführt:

var get = module.exports.get = function get(object, path) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return;
  }

  if (path.length === 0) {
    return object[key];
  }

  if (path.length) {
    return get(object[key], path);
  }
};
1
Paul 19 Juli 2013 im 15:21

Hier ist eine kurze ES6-Implementierung mit reduce:

function get(obj, keyPath) {
    return keyPath
        .split(".")
        .reduce((prev, curr) => prev[curr], obj);
}

Verwendung:

get(cars, "bentley.suppliers.0.name") // -> "Sheffield Mines"
0
Andris Lapsa 14 Sept. 2016 im 12:29