ES6'da nesne özelliklerini anahtar ile filtreleme


266

Diyelim ki bir nesnem var:

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

Yukarıdaki nesneyi filtreleyerek başka bir nesne oluşturmak istiyorum.

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

Es6 kullanarak bunu başarmak için temiz bir yol arıyorum, bu yüzden yayılmış operatörler benim için kullanılabilir.


ES6'da nesne yayma operatörü yoktur ve burada bunlara ihtiyacınız yoktur
Bergi


@DanDascalescu Ama bu cevap bir ES6'ya OP'nin istediklerini gerçekleştirmenin bir yolunu veriyor, değil mi?
Jonathan H

Bir anahtar / değere göre filtrelemek istersem ne olur?
jmchauv

Yanıtlar:


503

İzin verilen değerlerin bir listeniz varsa, bunları kullanarak bir nesnede kolayca tutabilirsiniz:

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

Bu kullanır:

  1. Object.keysraw(orijinal veriler) içindeki tüm özellikleri listelemek için
  2. Array.prototype.filter izin verilen listede bulunan tuşları seçmek için
    1. Array.prototype.includes mevcut olduklarından emin olmak için
  3. Array.prototype.reduce yalnızca izin verilen özelliklere sahip yeni bir nesne oluşturmak için.

Bu, izin verilen özelliklerle sığ bir kopya oluşturur (ancak özellikleri kendileri kopyalamaz).

Nesne yayma işlecini , bunları değiştirmeden bir dizi nesne oluşturmak için de kullanabilirsiniz ( bundan bahsettiği rjerue sayesinde ):

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

Eğer (Ben orijinal verilerden istenmeyen alanları kaldırmak istiyorsa trivia amaçları doğrultusunda, olmaz bazı çirkin mutasyonları içerdiğinden, yapıyor tavsiye) kullanarak, ters olabilir includesşöyle çeki:

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

Mutasyon tabanlı bir çözüm göstermek için bu örneği ekliyorum, ancak kullanmayı önermiyorum.


1
Harika çalıştı teşekkürler. Ben de 'yapısöküm sözdizimini kullanarak bir yaklaşım buldum. IE: const {item1, item3} = ham const newObject = {item1, item3}
29er

2
Yapısızlaştırma işe yarayacak (gayet iyi), ama sadece derleme zamanı. Bunu dinamik bir özellikler listesi yapamaz veya karmaşık kurallar sağlayamazsınız (örneğin, bir döngüye doğrulama geri çağrıları eklenebilir).
ssube

3
Buna yeterince oy veremem! Filtre kullanmak ve azaltmak ve bir for döngüsünden başka bir nesne oluşturmamak için aferin. Ve değişmez ve değişmez sürümleri açıkça ayırdığınız harika. +1
Sukima

3
Hızlı uyarı: Array.prototype.includesES6'nın bir parçası değildir. ECMAScript 2016'da (ES7) tanıtıldı.
Vine


93

ES6 sözdizimini kullanarak sorun yaşıyorsanız, burada ve burada belirtildiği gibi bunu yapmanın en temiz yolunun :

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

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

Şimdi newDataşunları içerir:

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

Veya anahtarı dize olarak depoladıysanız:

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

İkinci durumda, [key]dönüştürülür item2ancak bir constödev kullandığınız için ödev için bir ad belirtmeniz gerekir. _atma değerini temsil eder.

Daha genel olarak:

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

Bu sadece operasyonunuzu tek astarlı bir şekilde azaltmakla kalmaz, aynı zamanda diğer anahtarların ne olduğunu (korumak istediğiniz anahtarları) bilmenizi de gerektirmez.


5
" _atılma değerini temsil eder" bu nereden geliyor? İlk gördüğümde
yhabib

5
Bunun JS topluluğu tarafından kabul edilmiş bir sözleşme olduğuna inanıyorum. An _, JS'de kullanılabilen geçerli bir değişken adıdır, ancak neredeyse isimsiz olduğundan, niyet iletmeyi önemsiyorsanız gerçekten bu şekilde kullanılmamalıdır. Bu yüzden, umursadığınız bir değişkeni belirtmenin bir yolu olarak benimsenmiştir. İşte bunun hakkında daha fazla tartışma: stackoverflow.com/questions/11406823/…
Ryan H.

2
Bu, kabul edilen cevap çok daha derli toplu olduğunu Object.keys (içeren yeni bir dizi oluşturma yükünü) ve birlikte diziyi yinelemek yükünden kaçınmak filterve reduce.
ericsoco

3
@yhabib _önemli değil, sadece değişken bir isim, istediğiniz herhangi bir adla yeniden adlandırabilirsiniz
Vic

1
@Gerrat Genelleştirilmiş bir çözümün önemsiz bir astar olacağını düşünmüyorum. Bunun için, lodash'ın omitişlevini kullanırım: lodash.com/docs/4.17.10#omit veya burada verilen diğer çözümlerden biri.
Ryan

42

Bulabilmenin en temiz yolu 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
Bunun için çalışma zamanında fazladan bir proje bağımlılığının indirilmesi gerektiğini belirtmek önemlidir.
S. Esteves

1
_.omit (obj, ["item2"]) - lodash.omit, lodash.pick'in tam tersidir
Alexander Solovyev

29

Daha önce söylenmeyen bir şey yok, ancak genel bir ES6 yanıtına verilen bazı cevapları birleştirmek için:

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
Bu diğerlerinden çok daha basit ve performanslı bir çözümdür;)
Ocak'ta

26

Harici kütüphanesi olmayan Modern JS'nin bir satırında başka bir çözüm .

" Yıkıcı " özelliği ile oynuyordum :

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);Sözdizimi sorunu
Awol

1
Burada yoğunlaşan birkaç şey var: İlk olarak, fonksiyonun beyanı var. bir ok işlevidir (bir nesneyi alır ve bir nesneyi döndürür). Bu işlev (obj) {return obj;} ile aynı şeydir. ikinci şey, ES6'nın geleceği yıkmaya izin vermesidir. İşlev bildirimimde {item1, item3} nesnesimi imha ediyorum. Ve son olarak işlevimi kendim çağırıyorum. Örneğin, kapsamınızı yönetmek için kendi kendine çağırma işlevini kullanabilirsiniz. Ama burada sadece kodu yoğunlaştırmaktı. Umarım açıktır. Eğer bir şeyi özlüyorum daha eklemek için çekinmeyin.
Novy

5
tercih edilen modern yaklaşım bu
imho

20

Artık Object.fromEntries yöntemini kullanarak daha kısa ve basit hale getirebilirsiniz (tarayıcı desteğini kontrol edin):

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

hakkında daha fazla bilgi için: Object.fromEntries


1
Şimdi bence en iyi yol bu. Azaltmaktan çok daha sezgisel.
Damon

10

Nesneleri, diziler gibi kolayca filtreleyebilmek için bir genel ofilter( genel ile uygulanmış oreduce) ekleyebilirsiniz -

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
    )

Burada çalıştığını görebiliriz -

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

Sonuçları aşağıda kendi tarayıcınızda doğrulayın -

Bu iki fonksiyon birçok şekilde uygulanabilir. Array.prototype.reduceİçine takmayı seçtim oreduceama hepsini en baştan kolayca yazabilirsiniz


Çözümünüz gibi, ama karşılaştırıldığında ne kadar net / daha verimli bilmiyorum bu bir .
Jonathan H

3
İşte çözümünüzün en hızlı olduğunu gösteren bir karşılaştırma ölçütü .
Jonathan H

Gelen oreduceilk accyılında gölgeli .reduce(acc, k)ve içinde gölgeli - Bir değişkenle denir denilen hangisinin hangisi - sıra? ofilterooreduceo
Jarrod Mosen

içinde ofilterdeğişken ogölgeli ama her zaman aynı giriş var işaret; bu yüzden burada gölgeli, çünkü aynı - akümülatör ( acc) de gölgeli çünkü verilerin lambda üzerinden nasıl hareket ettiğini daha açık gösteriyor; accher zaman aynı bağlayıcı değildir , ancak her zaman geri dönmek istediğimiz hesaplama sonucunun mevcut kalıcı durumunu temsil eder
Teşekkürler

Kod iyi çalışıyor ve yöntem çok iyi olsa da, böyle bir kod yazma yardım javascript ile yardıma ihtiyacı olan birine ne olduğunu merak ediyorum. Ben kıskançlık için değilim ama bu en aza indirilmiş bir kod kadar kompakt.
Paul G Mihai

7

Son zamanlarda şöyle yaptım:

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

Hm. Görünüşe göre eski ve yeni sözdizimini karıştırıyorsunuz. Object.assign == ...Sen yazabilirsiniz const dummyObj = { ...obj }ve const target = { ...dummyObj }. Ayrıca, ikincisi de gerekli değildir, çünkü daha sonra dummyObj ile doğrudan çalışabilirsiniz.
Andy

7

tamam, bu tek astarlı

    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
Tüm cevapların en şaşırtıcı çözümü. +1
Gergő Horváth

ya sadece kaldırmak istediğiniz anahtarları biliyorsanız ... saklamak istediğiniz anahtarları değil mi?
Jason

6

Buradaki cevaplar kesinlikle uygundur, ancak nesnelerdeki her özellik için beyaz listeden döngü gerektirdikleri için biraz yavaştırlar. Aşağıdaki çözüm, büyük veri kümeleri için çok daha hızlıdır çünkü beyaz listeden yalnızca bir kez geçer:

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

Ssube'nin telesekreterinde piggybacking .

İşte yeniden kullanılabilir bir versiyon.

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

Aramak için şunu kullanın:

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

ES6 ok işlevleri ile ilgili güzel olan şey, allowedparametre olarak geçmeniz gerekmemesidir .


4

Bunun gibi bir şey yapabilirsiniz:

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

Bu işe yarıyor ancak çok net değil, ayrıca withifade önerilmez ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with ).


4

Kullanmadan daha basit bir çözüm filterelde edilebilir Object.entries().Object.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

Bunu başarmanın birçok yolu vardır. Kabul edilen cevap , en performanslı olmayan bir Anahtar-Filtre-Küçültme yaklaşımı kullanır.

Bunun yerine, for...inbir nesnenin anahtarları arasında döngü yapmak için bir döngü kullanmak veya izin verilen anahtarlar arasında döngü yapmak ve sonra yeni bir nesne oluşturmak ~% 50 daha fazla performansa sahiptir a .

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

a. Basit bir kullanım senaryosunun karşılaştırmaları için jsPerf'e bakın . Sonuçlar tarayıcılara göre farklılık gösterir.


3

Nesnenizdeki belirli bir anahtarı kaldırabilirsiniz

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
OP'nin sorusunu sağlayan başka cevaplar da var ve bunlar bir süre önce yayınlandı. Bir yanıt gönderirken, lütfen özellikle eski soruları yanıtlarken yeni bir çözüm veya çok daha iyi bir açıklama eklediğinizden emin olun.
help-info.de

2

Basit yol! Bunu yapmak için.

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


Burada hiçbir şeyi filtrelemiyorsunuz, sadece verilen verileri yok ediyorsunuz. Ancak veriler değiştiğinde ne olur?
Idris Dopico Peña

2

"Yeni" Array.reduceyöntemi kullanan başka bir çözüm :

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

Bu Kemanda Gösteri ...


Ama çözümü gibi burada bu cevap kullanıyor ve :Object.fromEntries Array.filterArray.includes

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

Bu Kemanda Gösteri ...


Wilt, ikinci yaklaşımınız geçen yıl verilen bu cevabın tam bir kopyası: stackoverflow.com/a/56081419/5522000
Art Schmidt

@ArtSchmidt Yorumunuz için teşekkür ederiz: Uzun listede bir cevapsız. Bunun yerine bu cevaba değineceğim.
Wilt

1

Tamam, buna ne dersin:

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

ve şöyle deyin:

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

1

Bu işlev, bir nesneyi anahtar listesine dayalı olarak filtreleyecektir, azaltma çağrılmadan önce Array.filter kullanması gerekmediği için önceki yanıttan daha verimlidir. böylece O (n), O (n + filtrelenmiş) yerine

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

1

Döngü sırasında, belirli özellikler / anahtarlarla karşılaşıldığında hiçbir şey döndürmeyin ve geri kalanıyla devam edin:

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

Henüz kimsenin bunu önermediğine şaşırdım. Süper temiz ve tutmak istediğiniz anahtarlar hakkında çok açık.

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

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

Veya kirli bir astar istiyorsanız:

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

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

Bu yöntem, orijinal dizide bulunmayan bir anahtar ekleyecektir. Bir sorun olabilir ya da olmayabilir, bunun en azından belirtilmesi gerekir.
ponchietto

0

Başka bir yaklaşım Array.prototype.forEach(),

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)

Yalnızca ham verilerde bulunan ve böylece gereksiz verilerin eklenmesini önleyen anahtarların değerlerini içerir.


0

Benim çözümüm bu olurdu:

const filterObject = (obj, condition) => {
    const filteredObj = {};
    Object.keys(obj).map(key => {
      if (condition(key)) {
        dataFiltered[key] = obj[key];
      }
    });
  return filteredObj;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.