javascript - Filtrer objektets egenskaper etter nøkkel i ES6


La oss si at jeg har et objekt:

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

Jeg vil lage et annet objekt ved å filtrere objektet ovenfor, slik at jeg har noe som.

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

Jeg leter etter en ryddig måte å oppnå dette på ved å bruke Es6, så spread-operatører er tilgjengelige for meg.

Svar #1

Hvis du har en liste over tillatte verdier, kan du enkelt beholde dem i et objekt ved å bruke:

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);

Dette bruker:

  1. Object.keys for å liste alle egenskaper i raw (de opprinnelige dataene), deretter
  2. Array.prototype.filter for å velge nøkler som er til stede i den tillatte listen, ved å bruke

    1. Array.prototype.includes for å forsikre seg om at de er til stede
  3. Array.prototype.reduce for å bygge et nytt objekt med bare de tillatte egenskapene.

Dette vil lage en grunn kopi med de tillatte egenskapene (men vil ikke kopiere egenskapene selv).

Du kan også bruke objektspredningsoperatoren for å lage en serie med objekter uten å endre dem (takk til rjerue for å nevne dette):

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);

For formål med trivia, hvis du ønsket å fjerne de uønskede feltene fra de opprinnelige dataene (noe jeg ikke ville anbefale å gjøre, siden det innebærer noen stygge mutasjoner), kunne du invertere includes-sjekken slik:

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);

Jeg inkluderer dette eksemplet for å vise en mutasjonsbasert løsning, men jeg anbefaler ikke å bruke den.

Svar #2

Hvis du er ok med å bruke ES6-syntaks, finner jeg at den reneste måten å gjøre dette på, som nevnt her og her, er:

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

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

Nå inneholder newData:

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

Eller, hvis du har nøkkelen lagret som en streng:

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

I det siste tilfellet blir [key] konvertert til item2, men siden du bruker en const-tilordning, må du spesifisere et navn for tilordningen. _ representerer en verdi som kastes bort.

Mer generelt:

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, ...

Dette reduserer ikke bare operasjonen din til en én-liners, men det krever heller ikke at du vet hva de andre nøklene er (de du ønsker å bevare).

En enkel hjelpefunksjon vil se slik ut:

function removePropFromObject(obj, prop) {
  const { [prop]: _, ...rest } = obj
  return { ...rest }
}

Svar #3

Du kan nå gjøre det kortere og enklere ved å bruke Object.fromEntries-metoden (sjekk nettleserstøtte):

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)
   )
);

les mer om: Object.fromEntries

Svar #4

Ingenting som ikke har blitt sagt før, men for å kombinere noen svar til et generelt ES6-svar:

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);

Svar #5

Den reneste måten du kan finne er med 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)

Svar #6

Bare en annen løsning i en linje med moderne JS uten eksterne biblioteker.

Jeg lekte med «Destructuring»-funksjonen :

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);

Svar #7

Bygger videre på disse to svarene:

https://stackoverflow.com/a/56081419/13819049
https://stackoverflow.com/a/54976713/13819049

Vi kan gjøre:

const original = { a: 1, b: 2, c: 3 };
const allowed = ['a', 'b'];

const filtered = Object.fromEntries(allowed.map(k => [k, original[k]]));

Hvilken er renere og raskere:

https://jsbench.me/swkv2cbgkd/1

Svar #8

ok, hva med denne enlinjers

    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]})));

Svar #9

En annen løsning som bruker Array.reduce-metoden på de tillatte nøklene:

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);

Spesielt i tilfelle større kildeobjekter (rå i dette eksempelet) vil det gi mening. Iterasjonen vil ikke bli utført ved bruk av alle oppføringene i kilden, men bare ved bruk av nøklene du ønsker å filtrere, og dermed kortere/raskere...

Demonstrasjon i denne Fiddle...


Men jeg må også si at jeg liker løsningen i dette svaret her, som bruker Object.fromEntries, Array.filter og Array.includes:

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

Demonstrasjon i denne Fiddle...

Svar #10

Du kan legge til en generell ofilter (implementert med generell oreduce) slik at du enkelt kan filtrere objekter på samme måte som du kan med arrays -

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
    )

Vi kan se det fungere her -

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 } } ]
  )

Bekreft resultatene i din egen nettleser nedenfor -

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
    )

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 } } ]
  )

Disse to funksjonene kan implementeres på mange måter. Jeg valgte å knytte til Array.prototype.reduce inne i oreduce, men du kunne like gjerne skrevet alt fra bunnen av

Svar #11

Svarene her er definitivt passende, men de er litt trege fordi de krever at man må gå gjennom hvitelisten for hver egenskap i objektet. Løsningen nedenfor er mye raskere for store datasett fordi den bare går gjennom hvitelisten én gang:

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,
    {}
  );
}

const result = sanitize(data, whitelist);

console.log(result);

Relaterte artikler