Javascript-Objektplatzierung / Zeichenfolge-Analysemethode

609
Zack Bloom

Diese JS-Funktion soll einen Wert mit den verschachtelten Schlüsseln als Zeichenfolge abrufen oder in ein Objekt einfügen.

Zum Beispiel

var obj = {a: {b: [4]}};
parse_obj_key(obj, "a.b.0") should equal 4.
parse_obj_key(obj, "a.c", 2) should add another element to "a" named "c" with value 2.

Die Methode funktioniert, aber ich möchte sie nach Möglichkeit aufräumen (oder eine ausgefeiltere Implementierung, die öffentlich verfügbar ist). Ich würde auch sehr gerne über mögliche Fehler im Edge-Case Bescheid wissen.

function parse_obj_key(obj, loc, val){
    var _o = obj;
    while (true){ 
        var pos = loc.indexOf('.');                        

        if (!_o || typeof _o != 'object'){
            $.log("Invalid obj path: " + loc + "\n" + JSON.stringify(obj));
            return null;      
        }                                                    

        if (pos === -1){
            if (val){
                _o[loc] = val;                 
                return obj;                                           
            } else {
                if (!isNaN(parseInt(loc)))
                    loc = parseInt(loc);

                return _o[loc];
            }
        }
        var part = loc.substring(0, pos);                    
        var loc = loc.substring(pos + 1);                     

        if (!isNaN(parseInt(part)))
            part = parseInt(part);   

        if (!(part in _o)){
            if (val)
                _o[part] = new object;                                        
            else
                return null;                                           
        }
        _o = _o[part];                      
    }                                                                         
}
Antworten
5
Ich würde vorschlagen, zwei verschiedene Methoden bereitzustellen, eine zu erhalten und eine, um Werte festzulegen (möglicherweise rufen beide eine allgemeine private Methode auf, um die Eigenschaft abzurufen). Eric Bréchemier vor 9 Jahren 0

3 Antworten auf die Frage

3
glebm

Folgendes ist mit Ihrem Code falsch:

  • Verwenden Sie keine Variablennamen wie _o. Holen Sie sich einen Editor mit guter Autovervollständigung.

  • typeof _o != 'object'nicht tut, was Sie denken, es tut: typeof([1,2]) // "object". Im Allgemeinen ist das Durchführen dieser Überprüfungen ein Codegeruch.

  • if (!isNaN(parseInt(loc))) loc = parseInt(loc);. Verwirrend und nicht erforderlich.
    JavaScript: ['a', 'b']["1"] // 'b'. Gleiches gilt für den anderenisNaN

  • in. Tun Sie das nicht. nullist ein Wert, aber was Sie zurückgeben möchten, ist der Mangel an Wert. Es ist undefinedin JavaScript und wird zurückgegeben, wenn es keinen Wert gibt.

  • Verwenden Sie splitanstelle von indexOfund substring. Es ist viel schneller und macht den Code lesbarer.

Hier ist eine nette Version für Sie:

function chained(obj, chain, value){
    var assigning = (value !== undefined);
    // split chain on array and property accessors
    chain = chain.split(/[.\[\]]+/);       
    // remove trailing ']' from split   
    if (!chain[chain.length - 1]) chain.pop();
    // traverse 1 level less when assigning
    var n = chain.length - assigning; 
    for (var i = 0, data = obj; i < n; i++) {
        data = data[chain[i]];
        // if (data === undefined) return; // uncomment to handle bad chain keys      
    }

    if (assigning) {
        data[chain[n]] = value;
        return obj;
    } else {
        return data;       
    }
}

Blogged: http://glebm.blogspot.com/2011/01/javascript-chained-nested-assignment.html

Bitte kommen Sie mit weiteren Verbesserungen :)

Könnten Sie erläutern, wie Ihre Lösung eine Verbesserung in Bezug auf den OP-Code darstellt? Eric Bréchemier vor 9 Jahren 0
@Eric Done Fühlen Sie sich frei, um mehr hinzuzufügen. glebm vor 9 Jahren 0
Ich stimme Ihren Kommentaren meistens zu, außer "Verwenden Sie keinen strengen Vergleich". Ich würde eher vorschlagen, die ganze Zeit einen strengen Vergleich zu verwenden und sich auf "The Good Parts" (c) von JavaScript anstatt auf seine Macken zu konzentrieren. +1 Eric Bréchemier vor 9 Jahren 2
Ich habe _o verwendet, um anzuzeigen, dass es sich um eine temporäre Variable handelt, die nur in der Schleife durcheinander gebracht wird, z. B. dass sie nur für das eigentliche Objekt relevant ist. Ich wollte, dass der Typ so arbeitet, wie er gearbeitet hat. Ich möchte wissen, ob es sich um ein Array oder ein Objekt handelt (z. B. kein Primitiv). Wird dies nicht für diesen Zweck funktionieren? Zack Bloom vor 9 Jahren 0
Ich war nicht ganz richtig in Bezug auf Ihre Verwendung von typeof. Es wird geprüft, ob etwas ein Primitiv ist oder nicht. Wenn Sie jedoch `parse_obj_key (" awesome_string "," length ")` versuchen, funktioniert es nicht, da "object" `nicht dasselbe ist wie etwas, das auf Methoden reagiert. Prost. glebm vor 9 Jahren 0
Ich stimme Eric zu. `==` und `! =` sind böse. `0 == '' // true 0 == '0' // true '0' == '' // false!` Oder `'\ t \ r \ n \ f' == 0 // true` oder even '' ,,,,,, '== Array ((' i '<3,' js ',' auf einer Skala von 1 bis 10, js ist a ', 7)) `, obwohl dies mehr als nur' ausnutzt == `... Peter C vor 9 Jahren 0
Ich stimme auch mit Eric überein. Wie umarmt `===` die Sprache nicht? Sie müssen nur wissen, dass "domNode.property === 123" immer "false" ist. Verwenden Sie also "===" 123 "". Die Verwendung von "== 123" umfasst nicht die Sprache der IMO, sondern setzt nur auf einen Typ-Zwang, der langsamer ist und in manchen Fällen das Leben schwieriger macht: "dateInput == new Date ()" ... Außerdem "aVar" === + (aVar) `ist in JS _very_ üblich Elias Van Ootegem vor 7 Jahren 0
Ich habe das `==` Bit entfernt, da es Ihnen gut geht :) glebm vor 7 Jahren 0
0

Ich weiß wenig über Javascript und dachte, dass Ihre Lösung in Ordnung ist. glebms Code scheint nicht zu funktionierenparse_obj_key(obj, "a.c.c.c", 2)

Ich versuche, Ihren Code in einen Rekursionsstil zu ändern, und es funktioniert, das "neue Objekt" geht auf meinem Firefox schief, daher ändere ich es in = {}

function parse_obj_key(obj, loc, val) {
    if(!obj || typeof obj != 'object') {
        alert("error")
        return null
    }
    var pos = loc.indexOf('.');
    var part = loc.substring(0, pos);
    var endpart = loc.substring(pos+1);

    if (!isNaN(parseInt(part)))
        part = parseInt(part);

    if (pos === -1) {
        if (val) {
            obj[loc] = val
            return obj
        }
        else
            return obj[loc]
    }

    if (val) {
        if (!(part in obj)) 
            obj[part] = {}

        parse_obj_key(obj[part], endpart, val)
        return obj
    }
    else {
        return parse_obj_key(obj[part], endpart, val)
    }
}
Ich ziehe einen solchen rekursiven Stil dem "while (true)" vor, wenn Sie damit durchkommen können. Ich würde jedoch sagen, dass Sie die Validierung von 'obj' in eine separate Methode umwandeln möchten. `parse_obj_key` würde die Argumente überprüfen und dann die rekursive Hilfsfunktion aufrufen. Auf diese Weise werden die Argumente bei jeder Rekursion erneut validiert. In diesem Fall kein großes Problem, aber ein Muster, das für eine tiefere Rekursion oder für die Verschiebung komplexer Argumente zu berücksichtigen ist. ICR vor 9 Jahren 0
0
ICR

Mein Code basiert auf glebm's, aber ich habe ihn so geändert, dass Array-Lookups immer die .Syntax verwenden müssen. Ich habe einige weitere Verbesserungen vorgenommen, z. B. das Erstellen einer Objektkette, wenn einem verschachtelten Schlüssel zugewiesen wird, der noch nicht vorhanden ist.

function parse_obj_key(obj, key, value){
    var is_assigning = (value !== undefined);
    key = key.split('.');     

    var data = obj;
    var length = key.length - is_assigning;

    for (var i = 0; i < length; i++) {
        var child = data[key[i]];
        var has_child = (child !== undefined);

        if (!has_child && !is_assigning) {
            $.log("Invalid obj path: " + key + "\n" + JSON.stringify(obj));
            return null;
        }

        if (!has_child) {
            child = {};
        data[key[i]] = child;
        }

        data = child;
    }

    if (is_assigning) {
        data[key[i]] = value;
    } else {
        return data;       
    }
}

Wenn Sie versuchen, einen optionalen Parameter zu erstellen, sollten Sie prüfen, ob es nicht definiert ist mit if (param !== undefined), und nicht nur if (param), sonst werden Sie Schwierigkeiten einstellen falsy Werte haben wie false, null, 0und "".

Dieser Code kann unter anderem viel kürzer sein, als die Beobachtung, dass in Javascript arr['a']['b'][0]das Gleiche ist wie arr['a']['b']['0']- das heißt, die Array-Suche mit einer Ganzzahl unterscheidet sich nicht von der Array-Suche mit einer Zeichenfolge, die eine Zahl enthält. Dadurch müssen Sie die Zahlen nicht wie Sie analysieren.

Der andere große Sparer ist die Verwendung von Split- anstelle von Indexierung, wodurch das forDurchlaufen der Teile des Schlüssels erleichtert wird .

Ihr Code gibt bei "parse_obj_key ({a: null}," a ")` nicht den Wert "null" zurück. Das Ersetzen von "(! Kind)" durch "Kind! == undefined" könnte dies beheben. Prost glebm vor 9 Jahren 0
Prost. Ich sollte auf meinen eigenen Rat hören: P ICR vor 9 Jahren 0