it-swarm.com.de

JavaScript: filter () für Objekte

ECMAScript 5 enthält den filter()-Prototyp für Array-Typen, aber nicht Object-Typen, wenn ich es richtig verstanden habe.

Wie würde ich eine filter() für Objects in JavaScript implementieren? 

Nehmen wir an, ich habe dieses Objekt:

var foo = {
    bar: "Yes"
};

Und ich möchte eine filter() schreiben, die auf Objects funktioniert:

Object.prototype.filter = function(predicate) {
    var result = {};

    for (key in this) {
        if (this.hasOwnProperty(key) && !predicate(this[key])) {
            result[key] = this[key];
        }
    }

    return result;
};

Dies funktioniert, wenn ich es in der folgenden Demo verwende, aber wenn ich es meiner Site hinzufüge, die jQuery 1.5 und jQuery UI 1.8.9 verwendet, erhalte ich JavaScript-Fehler in FireBug.

Object.prototype.filter = function(predicate) {
  var result = {};
  for (key in this) {
    if (this.hasOwnProperty(key) && !predicate(this[key])) {
      console.log("copying");
      result[key] = this[key];
    }
  }
  return result;
};

var foo = {
  bar: "Yes",
  moo: undefined
};

foo = foo.filter(function(property) {
  return typeof property === "undefined";
});

document.getElementById('disp').innerHTML = JSON.stringify(foo, undefined, '  ');
console.log(foo);
#disp {
  white-space: pre;
  font-family: monospace
}
<div id="disp"></div>

Erweitern Sie niemals Object.prototype.

Schreckliche Dinge passieren mit Ihrem Code. Die Dinge werden brechen. Sie erweitern all Objekttypen, einschließlich Objektliterale.

Hier ist ein kurzes Beispiel, das Sie ausprobieren können:

    // Extend Object.prototype
Object.prototype.extended = "I'm everywhere!";

    // See the result
alert( {}.extended );          // "I'm everywhere!"
alert( [].extended );          // "I'm everywhere!"
alert( new Date().extended );  // "I'm everywhere!"
alert( 3..extended );          // "I'm everywhere!"
alert( true.extended );        // "I'm everywhere!"
alert( "here?".extended );     // "I'm everywhere!"

Erstellen Sie stattdessen eine Funktion, die Sie das Objekt übergeben.

Object.filter = function( obj, predicate) {
    var result = {}, key;
    // ---------------^---- as noted by @CMS, 
    //      always declare variables with the "var" keyword

    for (key in obj) {
        if (obj.hasOwnProperty(key) && !predicate(obj[key])) {
            result[key] = obj[key];
        }
    }

    return result;
};
134
user113716

Zuallererst gilt es es ist schlechte Praxis, Object.prototype zu erweitern. Stellen Sie stattdessen Ihre Funktion als Dienstprogrammfunktion für Object bereit, so wie es bereits Object.keys, Object.assign, Object.is, ... etc gibt.

Ich biete hier mehrere Lösungen an:

  1. reduce und Object.keys verwenden
  2. Als (1) in Kombination mit Object.assign
  3. Verwenden von map und Spread-Syntax anstelle von reduce
  4. Object.entries und Object.fromEntries verwenden

1. Verwendung von reduce und Object.keys

Mit reduce und Object.keys den gewünschten Filter implementieren (mit ES6 Pfeilsyntax ):

Object.filter = (obj, predicate) => 
    Object.keys(obj)
          .filter( key => predicate(obj[key]) )
          .reduce( (res, key) => (res[key] = obj[key], res), {} );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

Beachten Sie, dass im obigen Code predicate eine Bedingung Inclusion sein muss (im Gegensatz zur Bedingung exclusion, die das verwendete OP verwendet), damit es der Funktionsweise von Array.prototype.filter entspricht.

2. Als (1) in Kombination mit Object.assign

In der obigen Lösung wird der Operator comma im Teil reduce verwendet, um das mutierte res-Objekt zurückzugeben. Dies könnte natürlich als zwei Anweisungen anstelle eines Ausdrucks geschrieben werden, der letztere ist jedoch kürzer. Um dies ohne den Komma-Operator zu tun, können Sie stattdessen Object.assign verwenden, wobei do das mutierte Objekt zurückgibt:

Object.filter = (obj, predicate) => 
    Object.keys(obj)
          .filter( key => predicate(obj[key]) )
          .reduce( (res, key) => Object.assign(res, { [key]: obj[key] }), {} );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

3. Verwenden Sie map und Spread-Syntax anstelle von reduce

Hier verschieben wir den Object.assign-Aufruf aus der Schleife, so dass er nur einmal gemacht wird, und übergeben die einzelnen Schlüssel als separate Argumente (mit der spread-Syntax ):

Object.filter = (obj, predicate) => 
    Object.assign(...Object.keys(obj)
                    .filter( key => predicate(obj[key]) )
                    .map( key => ({ [key]: obj[key] }) ) );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

4. Verwendung von Object.entries und Object.fromEntries

Da die Lösung das Objekt in ein Zwischenarray übersetzt und dieses dann wieder in ein einfaches Objekt konvertiert, ist es sinnvoll, Object.entries (ES2017) zu verwenden und eine Methode zu verwenden, die das Gegenteil tut (dh create) ein Objekt aus einem Array von Schlüssel/Wert-Paaren ). Zum Zeitpunkt des Schreibens befindet sich der Object.fromEntries-Vorschlag auf Stufe 3 und Firefox hat implementiert . Ansonsten kann ein Polyfill verwendet werden.

Es führt zu diesen beiden "Einzeiler" -Methoden auf Object (einschließlich der Polyfill):

Object.fromEntries = arr => Object.assign({}, ...arr.map( ([k, v]) => ({[k]: v}) ));
Object.filter = (obj, predicate) => 
                  Object.fromEntries(Object.entries(obj).filter(predicate));

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};

var filtered = Object.filter(scores, ([name, score]) => score > 1); 
console.log(filtered);

Die Prädikatsfunktion erhält hier ein Schlüssel/Wert-Paar als Argument, das etwas anders ist, jedoch mehr Möglichkeiten in der Logik der Prädikatsfunktion zulässt.

153
trincot

Wenn Sie Unterstrich oder lodash verwenden möchten, können Sie pick (oder dessen Gegenteil omit ) verwenden.

Beispiele aus den Unterlagen von Unterstrich:

_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
// {name: 'moe', age: 50}

Oder mit einem Rückruf (für lodash verwenden Sie pickBy ):

_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
  return _.isNumber(value);
});
// {age: 50}
18
Bogdan D

Wie bereits erwähnt, ist dies eine schlechte Idee, da mit Sicherheit fast jeder Code von Drittanbietern gebrochen wird, den Sie jemals verwenden möchten. 

Alle Bibliotheken wie Jquery oder Prototyp werden beschädigt, wenn Sie Object.prototype erweitern. Der Grund dafür ist, dass die verzögerte Iteration über Objekte (ohne hasOwnProperty-Prüfungen) unterbrochen wird, da die hinzugefügten Funktionen Teil der Iteration sind.

4

Ich habe ein Object.filter() erstellt, das nicht nur nach einer Funktion filtert, sondern auch ein Array von Schlüsseln zum Einschließen akzeptiert. Mit dem optionalen dritten Parameter können Sie den Filter invertieren.

Gegeben:

var foo = {
    x: 1,
    y: 0,
    z: -1,
    a: 'Hello',
    b: 'World'
}

Array:

Object.filter(foo, ['z', 'a', 'b'], true);

Funktion:

Object.filter(foo, function (key, value) {
    return Ext.isString(value);
});

Code

Disclaimer : Ich habe mich aus Gründen der Kürze für Ext JS Core entschieden. Ich hielt es nicht für notwendig, Typprüfer für Objekttypen zu schreiben, da dies nicht Teil der Frage war.

// Helper functions
function clone(obj) { return JSON.parse(JSON.stringify(obj)); }
function print(obj) {
    document.getElementById('disp').innerHTML += JSON.stringify(obj, undefined, '  ') + '<br />';
    console.log(obj);
}

Object.filter = function (obj, ignore, invert) {
    if (ignore === undefined) {
        return obj;   
    }
    invert = invert || false;
    var not = function(condition, yes) { return yes ? !condition : condition; };
    var isArray = Ext.isArray(ignore);
    for (var key in obj) {
        if (obj.hasOwnProperty(key) &&
                (isArray && not(!Ext.Array.contains(ignore, key), invert)) ||
                (!isArray && not(!ignore.call(undefined, key, obj[key]), invert))) {
            delete obj[key];
        }
    }
    return obj;
};

var foo = {
    x: 1,
    y: 0,
    z: -1,
    a: 'Hello',
    b: 'World'
}, bar = clone(foo);

print(Object.filter(foo, ['z', 'a', 'b'], true));
print(Object.filter(bar, function (key, value) {
    return Ext.isString(value);
}));
#disp {
    white-space: pre;
    font-family: monospace
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/extjs/4.2.1/builds/ext-core.min.js"></script>
<div id="disp"></div>

2
Mr. Polywhirl

Gegeben

object = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};

keys = ['firstname', 'age'];

dann :

keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});
// {firstname:'abd', age: 16}

// Helper
function filter(object, ...keys) {
  return keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});
  
};

//Example
const person = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};

// Expected to pick only firstname and age keys
console.log(
  filter(person, 'firstname', 'age')
)

2
Abdennour TOUMI

Wie wäre es mit:

function filterObj(keys, obj) {
  const newObj = {};
  for (let key in obj) {
    if (keys.includes(key)) {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

Oder...

function filterObj(keys, obj) {
  const newObj = {};
  Object.keys(obj).forEach(key => {
    if (keys.includes(key)) {
      newObj[key] = obj[key];
    }
  });
  return newObj;
}
1
shaunw

Wie alle sagten, schraub nicht mit dem Prototyp rum. Schreiben Sie stattdessen einfach eine Funktion. Hier ist meine Version mit lodash:

import each from 'lodash/each';
import get from 'lodash/get';

const myFilteredResults = results => {
  const filteredResults = [];

  each(results, obj => {
    // filter by whatever logic you want.

    // sample example
    const someBoolean = get(obj, 'some_boolean', '');

    if (someBoolean) {
      filteredResults.Push(obj);
    }
  });

  return filteredResults;
};
0
saran3h

ES6 Ansatz ...

Stellen Sie sich vor, Sie haben dieses Objekt unten:

const developers = {
  1: {
   id: 1,
   name: "Brendan", 
   family: "Eich"
  },   
  2: {
   id: 2,
   name: "Alireza", 
   family: "Dezfoolian"
 }
};

Erstellen Sie eine Funktion:

const filterObject = (obj, filter, filterValue) => 
   Object.keys(obj).reduce((acc, val) => 
   (obj[val][filter] === filterValue ? acc : {
       ...acc,
       [val]: obj[val]
   }                                        
), {});

Und nenne es:

filterObject(developers, "name", "Alireza");

und wird zurückkehren :

{
  1: {
  id: 1,
  name: "Brendan", 
  family: "Eich"
}
0
Alireza

In diesen Fällen verwende ich die Jquery $ .map, die Objekte behandeln kann. Wie bereits in anderen Antworten erwähnt, ist es nicht ratsam, native Prototypen zu ändern ( https://developer.mozilla.org/de-de/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes )

Im Folgenden finden Sie ein Beispiel für die Filterung, indem Sie einige Eigenschaften Ihres Objekts überprüfen. Es gibt das eigene Objekt zurück, wenn Ihre Bedingung wahr ist, oder undefined, falls nicht. Die undefined-Eigenschaft lässt diesen Datensatz aus Ihrer Objektliste verschwinden. 

$.map(yourObject, (el, index)=>{
    return el.yourProperty ? el : undefined;
});
0
Marcel Kohls

Meine meinte Lösung:

function objFilter(obj, filter, nonstrict){
  r = {}
  if (!filter) return {}
  if (typeof filter == 'string') return {[filter]: obj[filter]}
  for (p in obj) {
    if (typeof filter == 'object' &&  nonstrict && obj[p] ==  filter[p]) r[p] = obj[p]
    else if (typeof filter == 'object' && !nonstrict && obj[p] === filter[p]) r[p] = obj[p]
    else if (typeof filter == 'function'){ if (filter(obj[p],p,obj)) r[p] = obj[p]}
    else if (filter.length && filter.includes(p)) r[p] = obj[p]
  }
  return r
}

Testfälle:

obj = {a:1, b:2, c:3}

objFilter(obj, 'a') // returns: {a: 1}
objFilter(obj, ['a','b']) // returns: {a: 1, b: 2}
objFilter(obj, {a:1}) // returns: {a: 1}
objFilter(obj, {'a':'1'}, true) // returns: {a: 1}
objFilter(obj, (v,k,o) => v%2===1) // returns: {a: 1, c: 3}

https://Gist.github.com/khullah/872d5a174108823159d845cc5baba337

0
Z. Khullah