Filtern Sie die Objekteigenschaften nach Schlüssel in ES6


265

Angenommen, ich habe ein Objekt:

{
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

Ich möchte ein anderes Objekt erstellen, indem ich das obige Objekt filtere, damit ich so etwas habe.

 {
    item1: { key: 'sdfd', value:'sdfd' },
    item3: { key: 'sdfd', value:'sdfd' }
 }

Ich suche nach einem sauberen Weg, um dies mit Es6 zu erreichen, sodass mir Spread-Operatoren zur Verfügung stehen.


ES6 hat keine Objektverteilungsoperatoren, und Sie brauchen sie hier sowieso nicht
Bergi

1
Mögliches Duplikat von JavaScript: filter () für Objekte
Jonathan H

@DanDascalescu Aber diese Antwort bietet eine ES6-Möglichkeit, das zu erreichen, was das OP verlangt, nicht wahr ?
Jonathan H

Was ist, wenn ich nach einem Schlüssel / Wert filtern möchte?
jmchauv

Antworten:


502

Wenn Sie eine Liste zulässiger Werte haben, können Sie diese einfach in einem Objekt beibehalten, indem Sie:

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.keys(raw)
  .filter(key => allowed.includes(key))
  .reduce((obj, key) => {
    obj[key] = raw[key];
    return obj;
  }, {});

console.log(filtered);

Dies verwendet:

  1. Object.keysum dann alle Eigenschaften in raw(den Originaldaten) aufzulisten
  2. Array.prototype.filter um Schlüssel auszuwählen, die in der zulässigen Liste vorhanden sind, mit
    1. Array.prototype.includes um sicherzustellen, dass sie vorhanden sind
  3. Array.prototype.reduce um ein neues Objekt nur mit den zulässigen Eigenschaften zu erstellen.

Dadurch wird eine flache Kopie mit den zulässigen Eigenschaften erstellt (die Eigenschaften selbst werden jedoch nicht kopiert).

Sie können auch den Objektverteilungsoperator verwenden , um eine Reihe von Objekten zu erstellen, ohne sie zu mutieren (danke an rjerue für die Erwähnung ):

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.keys(raw)
  .filter(key => allowed.includes(key))
  .reduce((obj, key) => {
    return {
      ...obj,
      [key]: raw[key]
    };
  }, {});

console.log(filtered);

Wenn Sie aus Gründen der Trivia die unerwünschten Felder aus den Originaldaten entfernen möchten (was ich nicht empfehlen würde , da es sich um einige hässliche Mutationen handelt), können Sie die includesPrüfung wie folgt umkehren:

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

Object.keys(raw)
  .filter(key => !allowed.includes(key))
  .forEach(key => delete raw[key]);

console.log(raw);

Ich füge dieses Beispiel hinzu, um eine mutationsbasierte Lösung zu zeigen, empfehle jedoch nicht, sie zu verwenden.


1
Danke, das hat super funktioniert. Ich habe auch einen Ansatz gefunden, indem ich die Dekonstruktionssyntax verwendet habe. IE: const {item1, item3} = raw const newObject = {item1, item3}
29er

2
Die Dekonstruktion funktioniert (ganz gut), ist aber reine Kompilierungszeit. Sie können keine dynamische Liste von Eigenschaften erstellen oder komplexe Regeln bereitstellen (an eine Schleife können beispielsweise Validierungsrückrufe angehängt werden).
ssube

3
Ich kann das nicht genug abstimmen! Gut gemacht für die Verwendung von Filter und Reduzieren und nicht das Erstellen eines anderen Objekts aus einer for-Schleife. Und großartig, dass Sie die unveränderlichen und veränderlichen Versionen explizit getrennt haben. +1
Sukima

3
Kurzwarnung: Array.prototype.includesist nicht Teil von ES6. Es wurde in ECMAScript 2016 (ES7) eingeführt.
Vineet

3
Wenn Sie die Reduzierung auf unveränderliche Weise durchführen möchten, können Sie den Funktionsinhalt auch durch return {... obj, [key]: raw [key]}
rjerue

93

Wenn Sie mit der Verwendung der ES6-Syntax einverstanden sind, ist dies der sauberste Weg, wie hier und hier angegeben :

const data = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const { item2, ...newData } = data;

newDataEnthält jetzt :

{
  item1: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

Oder wenn Sie den Schlüssel als Zeichenfolge gespeichert haben:

const key = 'item2';
const { [key]: _, ...newData } = data;

Im letzteren Fall [key]wird in konvertiert, item2aber da Sie eine constZuweisung verwenden, müssen Sie einen Namen für die Zuweisung angeben. _repräsentiert einen Wegwerfwert.

Allgemeiner:

const { item2, ...newData } = data; // Assign item2 to item2
const { item2: someVarName, ...newData } = data; // Assign item2 to someVarName
const { item2: _, ...newData } = data; // Assign item2 to _
const { ['item2']: _, ...newData } = data; // Convert string to key first, ...

Dies reduziert nicht nur Ihren Betrieb auf einen Einzeiler, sondern erfordert auch nicht, dass Sie die anderen Schlüssel kennen (die, die Sie beibehalten möchten).


5
" _stellt einen Wegwerfwert dar" woher kommt das? Zum ersten Mal sehe ich es
Yhabib

5
Ich glaube, dies ist eine von der JS-Community verabschiedete Konvention. An _ist einfach ein gültiger Variablenname, der in JS verwendet werden kann. Da er jedoch so gut wie namenlos ist, sollte er nicht auf diese Weise verwendet werden, wenn Sie Absichten vermitteln möchten. Es wurde also verwendet, um eine Variable zu kennzeichnen, die Sie nicht interessieren. Hier ist eine weitere Diskussion darüber: stackoverflow.com/questions/11406823/…
Ryan H.

2
Dies ist viel aufgeräumter als die akzeptierte Antwort, vermeidet den Aufwand beim Erstellen eines neuen Arrays mit Object.keys () und den Aufwand beim Iterieren des Arrays mit filterund reduce.
Ericsoco

3
@ Yhabib das _spielt keine Rolle, es ist nur ein Variablenname, Sie können es in alles umbenennen, was Sie wollen
Vic

1
@Gerrat Ich denke nicht, dass eine verallgemeinerte Lösung ein trivialer Einzeiler wäre. Dafür würde ich die omitFunktion von lodash verwenden : lodash.com/docs/4.17.10#omit oder eine der anderen hier angegebenen Lösungen.
Ryan H.

42

Der sauberste Weg, den Sie finden können, ist mit Lodash # pick

const _ = require('lodash');

const allowed = ['item1', 'item3'];

const obj = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

const filteredObj = _.pick(obj, allowed)

3
Es ist wichtig darauf hinzuweisen, dass zur Laufzeit eine zusätzliche Projektabhängigkeit heruntergeladen werden muss.
S. Esteves

1
_.omit (obj, ["item2"]) - lodash.omit ist das Gegenteil von lodash.pick
Alexander Solovyev

29

Nichts, was vorher nicht gesagt wurde, aber um einige Antworten auf eine allgemeine ES6-Antwort zu kombinieren:

const raw = {
  item1: { key: 'sdfd', value: 'sdfd' },
  item2: { key: 'sdfd', value: 'sdfd' },
  item3: { key: 'sdfd', value: 'sdfd' }
};

const filteredKeys = ['item1', 'item3'];

const filtered = filteredKeys
  .reduce((obj, key) => ({ ...obj, [key]: raw[key] }), {});

console.log(filtered);


1
Dies ist eine viel einfachere und performantere Lösung als andere;)
Pomeh

26

Nur eine weitere Lösung in einer Zeile von Modern JS ohne externe Bibliotheken .

Ich habe mit der Funktion " Destructuring " gespielt:

const raw = {
    item1: { key: 'sdfd', value: 'sdfd' },
    item2: { key: 'sdfd', value: 'sdfd' },
    item3: { key: 'sdfd', value: 'sdfd' }
  };
var myNewRaw = (({ item1, item3}) => ({ item1, item3 }))(raw);
console.log(myNewRaw);


2
var myNewRaw = (({ item1, item3}) => ({ item1, item3 }))(raw);Syntaxproblem
Awol

1
Hier sind einige Dinge zusammengefasst: Als erstes gibt es die Deklaration der Funktion. Es ist eine Pfeilfunktion (es nimmt ein Objekt und gibt ein Objekt zurück). Es ist dasselbe wie function (obj) {return obj;}. Die zweite Sache ist, dass ES6 die Zukunft der Destrukturierung ermöglicht. In meiner Funktionsdeklaration zerstöre ich mein Objekt {item1, item3}. Und das Letzte ist, dass ich mich selbst auf meine Funktion berufe. Sie können beispielsweise die Selbstaufruffunktion verwenden, um Ihren Bereich zu verwalten. Aber hier war es nur, um den Code zu verdichten. Ich hoffe es ist klar. Wenn ich etwas vermisse, können Sie gerne weitere hinzufügen.
Novy

5
Dies ist der bevorzugte moderne Ansatz imho
mattdlockyer

20

Sie können es jetzt kürzer und einfacher machen, indem Sie die Object.fromEntries- Methode verwenden (überprüfen Sie die Browserunterstützung):

const raw = { item1: { prop:'1' }, item2: { prop:'2' }, item3: { prop:'3' } };

const allowed = ['item1', 'item3'];

const filtered = Object.fromEntries(
   Object.entries(raw).filter(
      ([key, val])=>allowed.includes(key)
   )
);

Lesen Sie mehr über: Object.fromEntries


1
Dies ist jetzt der beste Weg, den ich denke. Viel intuitiver als reduzieren.
Damon

10

Sie können ein Generikum hinzufügen ofilter(implementiert mit Generika oreduce), damit Sie Objekte auf die gleiche Weise wie Arrays filtern können.

const oreduce = (f, acc, o) =>
  Object
    .entries (o)
    .reduce
      ( (acc, [ k, v ]) => f (acc, v, k, o)
      , acc
      )

const ofilter = (f, o) =>
  oreduce
    ( (acc, v, k, o)=>
        f (v, k, o)
          ? Object.assign (acc, {[k]: v})
          : acc
    , {}
    , o
    )

Wir können sehen, dass es hier funktioniert -

const data =
  { item1: { key: 'a', value: 1 }
  , item2: { key: 'b', value: 2 }
  , item3: { key: 'c', value: 3 }
  }

console.log
  ( ofilter
      ( (v, k) => k !== 'item2'
      , data
      )
      // [ { item1: { key: 'a', value: 1 } }
      // , { item3: { key: 'c', value: 3 } }
      // ]

  , ofilter
      ( x => x.value === 3
      , data
      )
      // [ { item3: { key: 'c', value: 3 } } ]
  )

Überprüfen Sie die Ergebnisse in Ihrem eigenen Browser unten -

Diese beiden Funktionen können auf viele Arten implementiert werden. Ich habe mich entschieden, an der Array.prototype.reduceInnenseite zu befestigen, oreduceaber Sie könnten genauso gut alles von Grund auf neu schreiben


Ich mag Ihre Lösung, aber ich weiß nicht, wie viel klarer / effizienter sie im Vergleich zu dieser ist .
Jonathan H

3
Hier ist ein Benchmark, der zeigt, dass Ihre Lösung die schnellste ist.
Jonathan H

In oreducedem ersten accwird beschattet in .reduce(acc, k)und in ofilterder oabgeschattet wird - oreducemit einer Variablen namens heißt oauch - was das ist?
Jarrod Mosen

in ofilterder Variablen oist schattiert, zeigt aber immer auf dieselbe Eingabevariable; Deshalb ist es hier beschattet, weil es dasselbe ist - der Akkumulator ( acc) ist auch beschattet, weil er deutlicher zeigt, wie sich die Daten durch das Lambda bewegen. accist nicht immer die gleiche Bindung , aber es repräsentiert immer den aktuellen persistenten Zustand des Rechenergebnisses, das wir zurückgeben möchten
Danke

Obwohl der Code gut funktioniert und die Methode sehr gut ist, frage ich mich, welche Hilfe das Schreiben von Code für jemanden bedeutet, der offensichtlich Hilfe mit Javascript benötigt. Ich bin alles der Kürze halber, aber das ist fast so kompakt wie ein minimierter Code.
Paul G Mihai

7

So habe ich es kürzlich gemacht:

const dummyObj = Object.assign({}, obj);
delete dummyObj[key];
const target = Object.assign({}, {...dummyObj});

Hm. Es scheint, dass Sie alte und neue Syntax mischen. Object.assign == ...Du könntest schreiben const dummyObj = { ...obj }und const target = { ...dummyObj }. Letzteres ist überhaupt nicht notwendig, da Sie danach direkt mit dummyObj arbeiten können.
Andy

7

ok, wie wäre es mit diesem Einzeiler

    const raw = {
      item1: { key: 'sdfd', value: 'sdfd' },
      item2: { key: 'sdfd', value: 'sdfd' },
      item3: { key: 'sdfd', value: 'sdfd' }
    };

    const filteredKeys = ['item1', 'item3'];

    const filtered = Object.assign({}, ...filteredKeys.map(key=> ({[key]:raw[key]})));

2
Erstaunlichste Lösung aller Antworten. +1
Gergő Horváth

Was ist, wenn Sie nur die Schlüssel kennen, die Sie entfernen möchten ... nicht die Schlüssel, die Sie behalten möchten?
Jason

6

Die Antworten hier sind definitiv geeignet, aber sie sind etwas langsam, da sie die Whitelist für jede Eigenschaft im Objekt durchlaufen müssen. Die folgende Lösung ist für große Datenmengen viel schneller, da die Whitelist nur einmal durchlaufen wird:

const data = {
  allowed1: 'blah',
  allowed2: 'blah blah',
  notAllowed: 'woah',
  superSensitiveInfo: 'whooooah',
  allowed3: 'bleh'
};

const whitelist = ['allowed1', 'allowed2', 'allowed3'];

function sanitize(data, whitelist) {
    return whitelist.reduce(
      (result, key) =>
        data[key] !== undefined
          ? Object.assign(result, { [key]: data[key] })
          : result,
      {}
    );
  }

  sanitize(data, whitelist)

5

Huckepack auf ssubes Antwort .

Hier ist eine wiederverwendbare Version.

Object.filterByKey = function (obj, predicate) {
  return Object.keys(obj)
    .filter(key => predicate(key))
    .reduce((out, key) => {
      out[key] = obj[key];
      return out;
    }, {});
}

Um es zu nennen, verwenden Sie

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

var filtered = Object.filterByKey(raw, key => 
  return allowed.includes(key));
});

console.log(filtered);

Das Schöne an den ES6-Pfeilfunktionen ist, dass Sie keinen allowedParameter übergeben müssen.


4

Sie können so etwas tun:

const base = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const filtered = (
    source => { 
        with(source){ 
            return {item1, item3} 
        } 
    }
)(base);

// one line
const filtered = (source => { with(source){ return {item1, item3} } })(base);

Dies funktioniert, ist aber nicht sehr klar, und die withAnweisung wird nicht empfohlen ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with ).


4

Eine einfachere Lösung ohne Verwendung filterkann mit Object.entries()statt erreicht werdenObject.keys()

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.entries(raw).reduce((acc,elm)=>{
  const [k,v] = elm
  if (allowed.includes(k)) {
    acc[k] = v 
  }
  return acc
},{})

3

Es gibt viele Möglichkeiten, dies zu erreichen. Die akzeptierte Antwort verwendet einen Keys-Filter-Reduce-Ansatz, der nicht besonders leistungsfähig ist.

Stattdessen mit einer for...inSchleife zu Schleife durch Tasten eines Objekt oder die zulässigen Tasten Schleifen, und dann ein neues Objekt bildet , ist ~ 50% leistungsfähiger ein .

const obj = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const keys = ['item1', 'item3'];

function keysReduce (obj, keys) {
  return keys.reduce((acc, key) => {
    if(obj[key] !== undefined) {
      acc[key] = obj[key];
    }
    return acc;
  }, {});
};

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

keysReduce(obj, keys);   // Faster if the list of allowed keys are short
forInCompose(obj, keys); // Faster if the number of object properties are low

ein. In jsPerf finden Sie die Benchmarks eines einfachen Anwendungsfalls. Die Ergebnisse unterscheiden sich je nach Browser.


3

Sie können einen bestimmten Schlüssel für Ihr Objekt entfernen

items={
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

// Example 1
var key = "item2";
delete items[key]; 

// Example 2
delete items["item2"];

// Example 3
delete items.item2;

3
const filteredObject = Object.fromEntries(Object.entries(originalObject).filter(([key, value]) => key !== uuid))

2
Es gibt andere Antworten, die die Frage des OP stellen, und sie wurden vor einiger Zeit veröffentlicht. Stellen Sie beim Posten einer Antwort sicher, dass Sie entweder eine neue Lösung oder eine wesentlich bessere Erklärung hinzufügen, insbesondere wenn Sie ältere Fragen beantworten.
help-info.de

2

Einfacher Weg! Um dies zu tun.

const myData = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};
const{item1,item3}=myData
const result =({item1,item3})


Sie filtern hier eigentlich nichts, sondern zerstören nur die angegebenen Daten. Aber was passiert, wenn sich die Daten ändern?
Idris Dopico Peña

2

Eine andere Lösung mit der "neuen" Array.reduceMethode:

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = allowed.reduce((obj, key) => { 
  obj[key] = raw[key]; 
  return obj 
}, {})

console.log(filtered);

Demonstration in dieser Geige ...


Aber ich mag die Lösung in dieser Antwort hier, die verwendet und :Object.fromEntries Array.filterArray.includes

const object = Object.fromEntries( Object.entries(raw).filter(([key, value]) => allowed.includes(key)) );

Demonstration in dieser Geige ...


Wilt, Ihr zweiter Ansatz ist ein genaues Duplikat dieser Antwort aus dem letzten Jahr: stackoverflow.com/a/56081419/5522000
Art Schmidt

@ArtSchmidt Vielen Dank für Ihren Kommentar: Verpasste diesen in der langen Liste. Ich werde stattdessen auf diese Antwort verweisen.
Wilt

1

OK, wie wäre es damit:

const myData = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

function filteredObject(obj, filter) {
  if(!Array.isArray(filter)) {
   filter = [filter.toString()];
  }
  const newObj = {};
  for(i in obj) {
    if(!filter.includes(i)) {
      newObj[i] = obj[i];
    }
  }
  return newObj;
}

und nenne es so:

filteredObject(myData, ['item2']); //{item1: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' }}

1

Diese Funktion filtert ein Objekt basierend auf einer Liste von Schlüsseln. Dies ist effizienter als die vorherige Antwort, da Array.filter vor dem Aufruf von reduct nicht verwendet werden muss. also ist es O (n) im Gegensatz zu O (n + gefiltert)

function filterObjectByKeys (object, keys) {
  return Object.keys(object).reduce((accum, key) => {
    if (keys.includes(key)) {
      return { ...accum, [key]: object[key] }
    } else {
      return accum
    }
  }, {})
}

1

Geben Sie während der Schleife nichts zurück, wenn bestimmte Eigenschaften / Schlüssel angetroffen werden, und fahren Sie mit dem Rest fort:

const loop = product =>
Object.keys(product).map(key => {
    if (key === "_id" || key === "__v") {
        return; 
    }
    return (
        <ul className="list-group">
            <li>
                {product[key]}
                <span>
                    {key}
                </span>
            </li>
        </ul>
    );
});

1

Ich bin überrascht, wie noch niemand dies vorgeschlagen hat. Es ist super sauber und sehr explizit darüber, welche Schlüssel Sie behalten möchten.

const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}

const filterObject = ({a,b,c}) => ({a,b,c})
const filteredObject = filterObject(unfilteredObject)

Oder wenn Sie einen schmutzigen Einzeiler wollen:

const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}

const filteredObject = (({a,b,c})=>({a,b,c}))(unfilteredObject);

Diese Methode fügt einen Schlüssel hinzu, der im ursprünglichen Array nicht vorhanden ist. Könnte ein Problem sein oder nicht, darauf sollte zumindest hingewiesen werden.
Ponchietto

0

Ein anderer Ansatz wäre die Verwendung Array.prototype.forEach()als

const raw = {
  item1: {
    key: 'sdfd',
    value: 'sdfd'
  },
  item2: {
    key: 'sdfd',
    value: 'sdfd'
  },
  item3: {
    key: 'sdfd',
    value: 'sdfd'
  }
};

const allowed = ['item1', 'item3', 'lll'];

var finalObj = {};
allowed.forEach(allowedVal => {
  if (raw[allowedVal])
    finalObj[allowedVal] = raw[allowedVal]
})
console.log(finalObj)

Es enthält nur Werte der Schlüssel, die in den Rohdaten verfügbar sind, und verhindert so das Hinzufügen von Junk-Daten.


0

Das wäre meine Lösung:

const filterObject = (obj, condition) => {
    const filteredObj = {};
    Object.keys(obj).map(key => {
      if (condition(key)) {
        dataFiltered[key] = obj[key];
      }
    });
  return filteredObj;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.