Filtern Sie ein Objekt schnell nach Schlüsseln

139608
Mr. Polywhirl

Ich versuche, eine Objektfilterfunktion zu optimieren. In Anbetracht eines Arrays von Schlüsseln muss ich ein Objekt filtern. Wenn Sie jedes Mal ein neues Objekt erstellen, kann dies die Leistung beeinträchtigen. Gibt es eine Möglichkeit, die Array.prototype.filterFunktion für Objekte zu verspotten ?

function main() {
    var Types = {
        INTEGER:   { value: 'int'    },
        CHARACTER: { value: 'char'   },
        FLOAT:     { value: 'float'  },
        DOUBLE :   { value: 'double' },
        STRING:    { value: 'str'    },
        BOOLEAN:   { value: 'bool'   }
    };
    
    var Numbers = filterByKeys(Types, [ 'INTEGER', 'FLOAT', 'DOUBLE' ]);
    
    console.log(Numbers);
};

function listToSet(list) {
    var _set = {};
    if (Ext.isArray(list)) {
        for (var i = 0; i < list.length; i++) {
            _set[list[i]] = true;
        }
    }
    return _set;
};

function filterByKeys(obj, keep) {
    var result = {},
        unfiltered = keep === undefined,
        keys = listToSet(keep);
    if (Ext.isObject(obj)) {
        Ext.Array.each(Object.keys(obj), function(key) {
            if (unfiltered ||  (!unfiltered && keys[key])) {
                result[key] = obj[key];
            }
        });
    }
    return result;
};

Ext.onReady(function () {
    main();
});
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/extjs/4.2.1/ext-all.js"></script>

Ohne ExtJS-Abhängigkeit

function main() {
    var Types = {
        INTEGER:   { value: 'int'    },
        CHARACTER: { value: 'char'   },
        FLOAT:     { value: 'float'  },
        DOUBLE :   { value: 'double' },
        STRING:    { value: 'str'    },
        BOOLEAN:   { value: 'bool'   }
    };
    
    var Numbers = filterByKeys(Types, [ 'INTEGER', 'FLOAT', 'DOUBLE' ]);
    
    console.log(Numbers);
};

function listToSet(list) {
    var _set = {};
    if (isArr(list)) {
        for (var i = 0; i < list.length; i++) {
            _set[list[i]] = true;
        }
    }
    return _set;
};

function filterByKeys(obj, keep) {
    var result = {},
        unfiltered = keep === undefined,
        keys = listToSet(keep);
    if (isObj(obj)) {
        for (var key in obj) {
            if (unfiltered ||  (!unfiltered && keys[key])) {
                result[key] = obj[key];
            }
        };
    }
    return result;
};

var tObj='[object Object]',tArr='[object Array]',str=Object.prototype.toString;
var isArr='isArray'in Array?Array.isArray:function(v){return str.call(v)===tArr};
var isObj=str.call(null)===tObj?function(v){return v!==null&&v!==void 0&&str.call(v)===tObj&&v.ownerDocument===void 0}:function(v){return str.call(v)===tObj
};

$(document).ready(function () {
    main();
});
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Antworten
13
Wenn Sie bereits [lodash] (https://lodash.com) in Ihrem Toolkit haben, können Sie [`_.pick`] (https://lodash.com/docs/4.17.4#pick) für diese Aufgabe verwenden . John Rix vor 2 Jahren 0

3 Antworten auf die Frage

10
Jamal
  1. Verwenden Sie var myKeys = Object.keys(myObject), um die Schlüssel zu erhalten.
  2. Überprüfen Sie mithilfe von Native, ob ein myStringArray im Array vorhanden istmyKeys

    var matchingKey = myKeys.indexOf(myString) !== -1
    

    Array bearbeiten? Benutze das:

    var matchingKeys = myKeys.filter(function(key){ return key.indexOf(myString) !== -1 });
    
  3. Holen Sie sich den Wert mit myObject[matchingKey].

    Array bearbeiten? Benutze das:

    var matchingValues = matchingKeys .map(function(key){ return myObject[key] });
    
  4. Sie müssen keine Array-Grenzen oder -größen überprüfen. Es ist 100% grenzwertig.

Schritt 2. Kann vereinfacht werden `var matchingKey = myKeys.includes (myString)` und für die Array-Verarbeitung `var matchingKeys = myKeys.filter ((key) => key.includes (myString));` Tim vor 2 Jahren 0
Hallo @Jamal, in heutiger Zeit (2019!) ... Kein moderner EC6-Filter für Objekte? Peter Krauss vor einem Jahr 0
@ PeterKrauss: Ich bin der Herausgeber, nicht das Poster. Dieses Konto wurde seitdem trotzdem gelöscht. Jamal vor einem Jahr 0
5
wvxvw

Ich denke, die wichtigsten Fragen, die Sie sich stellen müssen, sind:

  1. Was versuche ich wirklich zu erreichen? (Die Beantwortung dieser Frage löst letztendlich Ihre Ungewissheit in Bezug darauf, ob Sie jedes Mal ein neues Objekt erstellen müssen oder möglicherweise Änderungen vornehmen usw.)
  2. Benutze ich die richtige Datenstruktur für die Aufgabe? Sie haben Ihre Datenstruktur aus Hash-Tabellen mit jeweils einem einzigen Schlüssel erstellt. Nicht nur das, derselbe Schlüssel wiederholt sich in jeder solchen Hash-Tabelle! Was würden Sie verlieren, wenn Sie statt der gesamten Hash-Tabelle nur den Wert dieser Hash-Tabelle verwenden würden?
  3. Dies ist zu einem Mantra geworden, aber es ist so wahr wie immer: Mach es funktionieren, mach es richtig, mach es schnell . Optimieren Sie nur, wenn Sie vom Profiler dazu aufgefordert werden. Und ganz sicher nicht, bevor Sie absolut überzeugt sind, dass Sie es richtig gemacht haben.

Als ein Beispiel, wie Ihre Datenstruktur anders aussehen könnte, um das Filtern einfacher zu gestalten:

var types = {
    'integer':   'int', 
    'character': 'char',
    'float':     'float',
    'double':    'double',
    'string':    'str', 
    'boolean':   'bool'
};

function filterTypes(accepted) {
    var result = {};
    for (var type in types)
        if (accepted.indexOf(type) > -1) 
            result[type] = types[type];
    return result;
}
// filterTypes(['integer', 'float', 'double']);
// { integer: 'int',
//   float: 'float',
//   double: 'double' }

Und das ist alles was dazu gehört. Ich würde auch den Zweck dieser Typen in JavaScript in Frage stellen, aber ich nahm an, dass sie nicht in JavaScript implementiert werden sollten, sondern lediglich eine Art von Bezeichnungen für eine andere Programmiersprache.

Beachten Sie außerdem, dass typeses nicht sinnvoll ist, die zu überprüfenden Schlüssel in eine andere Hash-Tabelle zu kopieren, da die Hash-Tabelle relativ klein ist (um eine schnellere Suche statt zu erreichen indexOf). Mit dieser Anzahl von Typen indexOfwird die Hashtabellensuche höchstwahrscheinlich noch übertroffen. Es bietet eine einfachere Lösung, die mit akzeptabler Geschwindigkeit arbeitet.

Aber heutzutage (2019!) ... Kein moderner EC6-Filter für Objekte? Peter Krauss vor einem Jahr 0
@ PeterKrauss Ich glaube, Sie meinten ES6, und ... Ich habe nicht den Standard zur Hand, aber aus MDN-Sicht: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference / Global_Objects / Object Es gibt keine Methode zum Filtern von Objektfeldern, es sei denn, ich bin blind. Ich vermute jedoch, dass das JS-Standardisierungsgremium nicht entscheiden kann, ob es sich bei '{}' nur um eine Hash-Tabelle handelt oder ob es sich eher um eine Klasseninstanz in anderen Sprachen handelt. Hin und wieder wechselt der Standard von einer Perspektive in eine andere, aber das hindert sie daran, sie wie eine Hash-Tabelle zu behandeln und diese Art von Funktionalität hinzuzufügen wvxvw vor einem Jahr 1
3
megawac
  1. unfiltered || (!unfiltered && keys[key]) ist überflüssig, was Sie gerade tun können unfiltered || keys[key]

  2. Sie werden für jede Eigenschaft festgelegt auf in Fehlalarme laufen Object.prototypewie "toString"oder "valueOf"(zum Beispiel betrachten {valueOf: 1})

  3. keys[key]ist nicht ausreichend, da er jeden Falsey-Wert (NaN, Null, Falsch, 0, "" usw.) verfehlt. Sie sollten entweder die inOperation oder verwendenhasOwnProperty

  4. Dies kann erheblich schneller (und genauer) durchgeführt werden, indem das keepArray anstelle der Eigenschaften durchläuft. obj(Damit können Sie einen Object.keysAnruf umgehen)


So würde ich es tun

// Avoid issues with `{hasOwnProperty: 5}`
var hasOwnProperty = ({}).hasOwnProperty;
function filterByKeys(obj, keep) {
    if (!(Ext.isObject(obj) && keep)) {
        return Ext.clone(obj);
    }

    var result = {};
    for (var i = 0, len = keep.length; i < len; i++) {
        var key = keep[i];
        if (hasOwnProperty.call(obj, key)) {
            result[key] = obj[key];
        }
    }

    return result;
};