Filter properti objek dengan kunci di ES6

266

Katakanlah saya punya objek:

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

Saya ingin membuat objek lain dengan memfilter objek di atas sehingga saya memiliki sesuatu seperti.

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

Saya mencari cara yang bersih untuk mencapai ini menggunakan Es6, jadi operator spread tersedia untuk saya.

29er
sumber
ES6 tidak memiliki operator penyebaran objek, dan Anda tidak memerlukannya di sini
Bergi
1
Kemungkinan rangkap dari JavaScript: filter () untuk Objects
Jonathan H
@DanDascalescu Tapi jawaban ini memberikan cara ES6 untuk mencapai apa yang diminta OP, bukan?
Jonathan H
Bagaimana jika saya ingin memfilter berdasarkan kunci / nilai?
jmchauv

Jawaban:

503

Jika Anda memiliki daftar nilai yang diizinkan, Anda dapat dengan mudah menyimpannya di objek menggunakan:

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

Ini menggunakan:

  1. Object.keysuntuk mendaftar semua properti di raw(data asli), lalu
  2. Array.prototype.filter untuk memilih kunci yang ada dalam daftar yang diizinkan, menggunakan
    1. Array.prototype.includes untuk memastikan mereka hadir
  3. Array.prototype.reduce untuk membangun objek baru dengan hanya properti yang diizinkan.

Ini akan membuat salinan dangkal dengan properti yang diizinkan (tetapi tidak akan menyalin properti itu sendiri).

Anda juga dapat menggunakan operator penyebaran objek untuk membuat serangkaian objek tanpa memutasikannya (terima kasih kepada rjerue karena menyebutkan ini ):

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

Untuk keperluan hal-hal sepele, jika Anda ingin menghapus bidang yang tidak diinginkan dari data asli (yang saya tidak akan merekomendasikan melakukannya, karena melibatkan beberapa mutasi buruk), Anda dapat membalikkan includescek seperti:

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

Saya menyertakan contoh ini untuk menunjukkan solusi berbasis mutasi, tetapi saya tidak menyarankan menggunakannya.

ssube
sumber
1
Terima kasih itu berhasil. Saya juga menemukan pendekatan dengan menggunakan sintaks dekonstruksi. IE: const {item1, item3} = raw const newObject = {item1, item3}
29er
2
Dekonstruksi akan berhasil (baik-baik saja), tetapi murni waktu kompilasi. Anda tidak dapat menjadikannya daftar properti yang dinamis atau memberikan aturan yang rumit (misalnya, sebuah loop dapat memiliki panggilan balik validasi yang dilampirkan).
ssube
3
Saya tidak bisa cukup memilih ini! Dilakukan dengan baik untuk menggunakan filter dan mengurangi dan tidak membangun objek lain dari for for. Dan luar biasa bahwa Anda secara eksplisit memisahkan versi yang tidak dapat diubah dan berubah-ubah. +1
Sukima
3
Peringatan cepat: Array.prototype.includesbukan bagian dari ES6. Ini diperkenalkan dalam ECMAScript 2016 (ES7).
Vineet
3
Jika Anda ingin melakukan pengurangan dengan cara yang tidak dapat diubah, Anda juga dapat mengganti konten fungsi dengan return {... obj, [key]: raw [key]}
rjerue
93

Jika Anda baik-baik saja dengan menggunakan sintaks ES6, saya menemukan bahwa cara terbersih untuk melakukan ini, seperti disebutkan di sini dan di sini adalah:

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

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

Sekarang, newDataberisi:

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

Atau, jika Anda memiliki kunci yang disimpan sebagai string:

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

Dalam kasus terakhir, [key]dikonversi menjadi item2tetapi karena Anda menggunakan consttugas, Anda harus menentukan nama untuk tugas tersebut. _mewakili nilai membuang.

Lebih umum:

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

Ini tidak hanya mengurangi operasi Anda menjadi satu-liner tetapi juga tidak mengharuskan Anda untuk mengetahui apa kunci lainnya (yang ingin Anda pertahankan).

Ryan H.
sumber
5
" _mewakili nilai membuang" di mana ini datang? Pertama kali saya melihatnya
yhabib
5
Saya percaya ini adalah konvensi yang diadopsi oleh komunitas JS. Sebuah _hanyalah nama variabel valid yang dapat digunakan dalam JS tetapi karena itu cukup banyak tanpa nama, itu benar-benar tidak boleh digunakan dengan cara itu jika Anda ingin menyampaikan maksud. Jadi, ini telah diadopsi sebagai cara untuk menunjukkan variabel yang tidak Anda pedulikan. Inilah diskusi lebih lanjut tentang hal itu: stackoverflow.com/questions/11406823/…
Ryan H.
2
Ini jauh lebih rapi daripada jawaban yang diterima, menghindari overhead menciptakan array baru dengan Object.keys (), dan overhead iterating array dengan filterdan reduce.
ericsoco
3
@yhabib _itu tidak masalah, itu hanya nama variabel, Anda dapat mengubah nama itu menjadi apa pun yang Anda inginkan
Vic
1
@ Gerrat Saya tidak berpikir solusi umum akan menjadi sepele satu-liner. Untuk itu, saya akan menggunakan omitfungsi lodash : lodash.com/docs/4.17.10#omit atau salah satu solusi lain yang diberikan di sini.
Ryan H.
42

Cara terbersih yang bisa Anda temukan adalah dengan 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)
Guy Segev
sumber
3
Sangat penting untuk menunjukkan bahwa untuk itu ketergantungan proyek tambahan harus diunduh saat runtime.
S. Esteves
1
_.omit (obj, ["item2"]) - lodash.omit adalah kebalikan dari lodash.pick
Alexander Solovyev
29

Tidak ada yang belum dikatakan sebelumnya, tetapi untuk menggabungkan beberapa jawaban untuk jawaban ES6 umum:

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

Clemens Helm
sumber
1
ini adalah solusi yang jauh lebih sederhana dan performan daripada yang lain;)
pomeh
26

Hanya solusi lain dalam satu baris Modern JS tanpa perpustakaan eksternal .

Saya bermain dengan fitur " Penghancuran ":

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

Novy
sumber
2
var myNewRaw = (({ item1, item3}) => ({ item1, item3 }))(raw);Masalah
sintaksis
1
Ada beberapa hal yang diringkas di sini: Hal pertama yang pertama, ada deklarasi fungsi. ini adalah fungsi panah (mengambil objek dan mengembalikan objek). Ini sama dengan fungsi (obj) {return obj;}. yang kedua adalah bahwa ES6 memungkinkan perusakan di masa depan. Dalam deklarasi fungsi saya, saya merusak objek saya {item1, item3}. Dan hal terakhir adalah saya sendiri menjalankan fungsi saya. Anda dapat menggunakan fungsi self invoke untuk mengelola ruang lingkup Anda misalnya. Tapi di sini hanya untuk menyingkat kode. Saya harap itu jelas. Jika saya melewatkan sesuatu jangan ragu untuk menambahkan lebih banyak.
Novy
5
ini adalah pendekatan modern yang disukai imho
mattdlockyer
20

Anda sekarang dapat membuatnya lebih pendek dan sederhana dengan menggunakan metode Object.fromEntries (periksa dukungan browser):

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

baca lebih lanjut tentang: Object.fromEntries

dudi harush
sumber
1
Sekarang ini cara terbaik yang saya pikirkan. Jauh lebih intuitif daripada mengurangi.
Damon
10

Anda dapat menambahkan generik ofilter(diimplementasikan dengan generik oreduce) sehingga Anda dapat dengan mudah menyaring objek dengan cara yang sama dengan array -

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
    )

Kita bisa melihatnya bekerja di sini -

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

Verifikasi hasil di browser Anda sendiri di bawah ini -

Kedua fungsi ini dapat diimplementasikan dengan berbagai cara. Saya memilih untuk melampirkan ke Array.prototype.reducedalam oreducetetapi Anda bisa dengan mudah menulis semuanya dari awal

Terima kasih
sumber
Saya suka solusi Anda, tetapi saya tidak tahu seberapa jauh lebih jelas / lebih efisien dibandingkan dengan yang ini .
Jonathan H
3
Berikut ini adalah tolok ukur yang menunjukkan bahwa solusi Anda adalah yang tercepat.
Jonathan H
Dalam oreduce, yang pertama accdibayangi di .reduce(acc, k), dan di ofilterdalam odibayangi - oreducedisebut dengan variabel yang disebut ojuga - yang mana?
Jarrod Mosen
dalam ofiltervariabel odibayangi tetapi selalu menunjuk ke input var yang sama; itu sebabnya dibayangi di sini, karena itu sama - akumulator ( acc) juga dibayangi karena lebih jelas menunjukkan bagaimana data bergerak melalui lambda; acctidak selalu mengikat yang sama , tetapi selalu mewakili kondisi persisten saat ini dari hasil komputasi yang ingin kami kembalikan
Terima kasih
Sementara kode bekerja dengan baik dan metodenya sangat bagus saya bertanya-tanya apa yang membantu menulis kode seperti itu untuk seseorang jelas membutuhkan bantuan dengan javascript. Saya semua untuk singkatnya tapi itu hampir sama kompaknya dengan kode yang diperkecil.
Paul G Mihai
7

Beginilah cara saya melakukannya, baru-baru ini:

const dummyObj = Object.assign({}, obj);
delete dummyObj[key];
const target = Object.assign({}, {...dummyObj});
Rajat Saxena
sumber
Hm Tampaknya Anda mencampur sintaks lama dan baru. Object.assign == ...Anda bisa menulis const dummyObj = { ...obj }dan const target = { ...dummyObj }. Plus, yang terakhir sama sekali tidak perlu, karena Anda bisa langsung bekerja dengan dummyObj sesudahnya.
Andy
7

ok, bagaimana dengan one-liner ini

    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]})));
inabramova
sumber
2
Solusi paling menakjubkan dari semua jawaban. +1
Gergő Horváth
bagaimana jika Anda hanya tahu kunci yang ingin Anda hapus ... bukan kunci yang ingin Anda simpan?
Jason
6

Jawaban di sini pasti cocok tetapi mereka agak lambat karena mereka memerlukan perulangan melalui daftar putih untuk setiap properti di objek. Solusi di bawah ini jauh lebih cepat untuk kumpulan data besar karena hanya melewati peritel sekali:

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)
Joey Grisafe
sumber
5

Membonceng answeranswer ssube ini .

Ini versi yang bisa digunakan kembali.

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

Untuk menyebutnya gunakan

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

Hal yang indah tentang fungsi panah ES6 adalah Anda tidak harus masuk allowedsebagai parameter.

Evan Plaice
sumber
4

Anda dapat melakukan sesuatu seperti ini:

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

Ini berfungsi tetapi tidak terlalu jelas, ditambah withpernyataan itu tidak direkomendasikan ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with ).

Bouni
sumber
4

Solusi yang lebih sederhana tanpa menggunakan filterdapat dicapai dengan Object.entries()alih - alihObject.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
},{})
Itai Noam
sumber
3

Ada banyak cara untuk mencapai ini. The jawaban yang diterima menggunakan Keys-Filter-Mengurangi pendekatan, yang bukan yang paling performant.

Alih-alih, menggunakan for...inloop untuk mengulang melalui kunci dari suatu objek, atau mengulang melalui kunci yang dibolehkan, dan kemudian menyusun objek baru adalah ~ 50% lebih banyak performan 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

Sebuah. Lihat jsPerf untuk tolok ukur kasus penggunaan sederhana. Hasil akan berbeda berdasarkan browser.

d4nyll
sumber
3

Anda dapat menghapus kunci spesifik pada objek Anda

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;
Esin ÖNER
sumber
3
const filteredObject = Object.fromEntries(Object.entries(originalObject).filter(([key, value]) => key !== uuid))
Konrad Albrecht
sumber
2
Ada jawaban lain yang memberikan pertanyaan OP, dan mereka diposting beberapa waktu lalu. Saat mengirim jawaban, pastikan Anda menambahkan solusi baru, atau penjelasan yang jauh lebih baik, terutama saat menjawab pertanyaan yang lebih lama.
help-info.de
2

Cara sederhana! Untuk melakukan ini.

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

ghanshyam bendkoli
sumber
Anda sebenarnya tidak memfilter apa pun di sini, hanya merusak data yang diberikan. Tetapi apa yang terjadi ketika data berubah?
Idris Dopico Peña
2

Solusi lain menggunakan metode "baru" Array.reduce:

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

Demonstrasi dalam Fiddle ini ...


Tapi saya suka solusi dalam jawaban ini di sini yang menggunakan dan :Object.fromEntries Array.filterArray.includes

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

Demonstrasi dalam Fiddle ini ...

Melayu
sumber
Wilt, pendekatan kedua Anda adalah duplikat tepat dari jawaban yang diberikan tahun lalu: stackoverflow.com/a/56081419/5522000
Art Schmidt
@ ArtSchmidt Terima kasih atas komentar Anda: Tidak ada dalam daftar panjang. Saya akan merujuk jawaban itu sebagai gantinya.
Layu
1

OK, bagaimana dengan ini:

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

dan menyebutnya seperti ini:

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

Fungsi ini akan memfilter objek berdasarkan daftar kunci, ini lebih efisien daripada jawaban sebelumnya karena tidak harus menggunakan Array.filter sebelum memanggil mengurangi. jadi itu O (n) sebagai lawan dari O (n + disaring)

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

Selama pengulangan, tidak mengembalikan apa pun ketika properti / kunci tertentu ditemukan dan melanjutkan sisanya:

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>
    );
});
Ryan Dhungel
sumber
1

Saya terkejut bagaimana belum ada yang menyarankan ini. Ini sangat bersih dan sangat eksplisit tentang kunci mana yang ingin Anda simpan.

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

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

Atau jika Anda ingin liner yang kotor:

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

const filteredObject = (({a,b,c})=>({a,b,c}))(unfilteredObject);
CCD Putih
sumber
Metode ini akan menambahkan kunci yang tidak ada dalam array asli. Mungkin menjadi masalah atau tidak, ini setidaknya harus ditunjukkan.
ponchietto
0

Pendekatan lain akan digunakan Array.prototype.forEach()sebagai

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)

Ini mencakup nilai hanya kunci-kunci yang tersedia dalam data mentah dan dengan demikian mencegah penambahan data sampah.

Saksham
sumber
0

Itu akan menjadi solusi saya:

const filterObject = (obj, condition) => {
    const filteredObj = {};
    Object.keys(obj).map(key => {
      if (condition(key)) {
        dataFiltered[key] = obj[key];
      }
    });
  return filteredObj;
}
Анна
sumber