Bagaimana cara memfilter array objek berdasarkan atribut?


473

Saya memiliki array JavaScript objek rumah real estat berikut:

var json = {
    'homes': [{
            "home_id": "1",
            "price": "925",
            "sqft": "1100",
            "num_of_beds": "2",
            "num_of_baths": "2.0",
        }, {
            "home_id": "2",
            "price": "1425",
            "sqft": "1900",
            "num_of_beds": "4",
            "num_of_baths": "2.5",
        },
        // ... (more homes) ...     
    ]
}

var xmlhttp = eval('(' + json + ')');
homes = xmlhttp.homes;

Apa yang ingin saya lakukan adalah dapat melakukan filter pada objek untuk mengembalikan subset objek "home".

Sebagai contoh, saya ingin dapat menyaring berdasarkan: price, sqft, num_of_beds, dan num_of_baths.

Bagaimana saya bisa melakukan sesuatu dalam JavaScript seperti pseudo-code di bawah ini:

var newArray = homes.filter(
    price <= 1000 & 
    sqft >= 500 & 
    num_of_beds >=2 & 
    num_of_baths >= 2.5 );

Catatan, sintaks tidak harus persis seperti di atas. Ini hanya sebuah contoh.


7
Ini tampaknya hampir identik dengan stackoverflow.com/questions/1694717/…
Crescent Fresh

3
var json = { ... }JSON adalah notasi tekstual untuk pertukaran data. (Lebih lanjut di sini.) Jika Anda berurusan dengan kode sumber JavaScript, dan tidak berurusan dengan string , Anda tidak berurusan dengan JSON.
TJ Crowder

1
Jangan gunakan eval. Ini umumnya merupakan praktik buruk dan dapat menyebabkan masalah kinerja. Kami hanya harus menyingkirkan sekelompok orang di proyek karena prosesor terkunci.
SDH

Jawaban:


719

Anda dapat menggunakan Array.prototype.filtermetode ini:

var newArray = homes.filter(function (el) {
  return el.price <= 1000 &&
         el.sqft >= 500 &&
         el.num_of_beds >=2 &&
         el.num_of_baths >= 2.5;
});

Contoh Langsung:

Metode ini adalah bagian dari ECMAScript 5th Edition yang baru , dan dapat ditemukan di hampir semua browser modern.

Untuk IE, Anda dapat menyertakan metode kompatibilitas berikut:

if (!Array.prototype.filter) {
  Array.prototype.filter = function(fun /*, thisp*/) {
    var len = this.length >>> 0;
    if (typeof fun != "function")
      throw new TypeError();

    var res = [];
    var thisp = arguments[1];
    for (var i = 0; i < len; i++) {
      if (i in this) {
        var val = this[i];
        if (fun.call(thisp, val, i, this))
          res.push(val);
      }
    }
    return res;
  };
}

8
apa / *, thisp * /?
JGreig

2
@ JGreig: Hanya komentar untuk menunjukkan bahwa argumen opsional dapat dilewati, argumen tidak ditentukan secara langsung karena standar ECMA secara tepat mengatakan bahwa metode ini seharusnya hanya mengharapkan satu argumen ( Array.prototype.filter.length == 1;). Saat Anda menggunakan argumen kedua, argumen itu akan digunakan sebagai thisnilai di dalam fungsi panggilan balik.
CMS

1
@CMS, Anda yakin kode ini berfungsi? Ini mengembalikan array kosong, bahkan ketika saya menetapkan harga menjadi sangat tinggi dan sqft / beds / baths menjadi sangat rendah. Apakah Anda yakin kode ini berfungsi?
JGreig

2
@JGreig: Ya, itu berfungsi, Anda harus memeriksa kriteria Anda, mungkin Anda bisa memposting contoh JSON yang lebih lengkap di pastie.org atau jsbin.com dan kriteria yang Anda gunakan untuk memfilter, sehingga saya dapat membantu Anda lebih baik.
CMS

2
@ CMS Akan lebih baik jika jawabannya mencakup fakta bahwa array baru dikembalikan, array asli tidak dimodifikasi. Meskipun ini dikatakan di tautan yang disediakan, saya menemukan jawabannya tanpa ini tidak lengkap atau tidak akurat
GWorking

29

Anda dapat mencoba menggunakan kerangka kerja seperti jLinq - berikut ini adalah contoh kode menggunakan jLinq

var results = jLinq.from(data.users)
.startsWith("first", "a")
.orEndsWith("y")
.orderBy("admin", "age")
.select();

Untuk informasi lebih lanjut, Anda dapat mengikuti tautan http://www.hugoware.net/projects/jlinq


26

Saya lebih suka kerangka garis bawah. Ini menyarankan banyak operasi berguna dengan objek. Tugas Anda:

var newArray = homes.filter(
    price <= 1000 & 
    sqft >= 500 &
    num_of_beds >=2 & 
    num_of_baths >= 2.5);

dapat ditimpa seperti:

var newArray = _.filter (homes, function(home) {
    return home.price<=1000 && sqft>=500 && num_of_beds>=2 && num_of_baths>=2.5;
});

Semoga bermanfaat bagi Anda!


Bagaimana cara menggarisbawahi ini bekerja dan apa artinya sebenarnya?
John Demetriou

4
Baru saja ditemukan underscore-query( github.com/davidgtonge/underscore-query ) yang menggunakan sintaksiseperti MongoDB untuk menanyakan array javascript. Jadi di sini Anda akan menggunakan_.query(homes, {price: {$lt:1000}, sqft: {$gte: 500}, num_of_beds: {$gte:2}, num_of_baths: {$gte: 2.5}}
prototipe

12

Saya terkejut tidak ada yang mengirim respons satu baris:

const filteredHomes = json.homes.filter(x => x.price <= 1000 && x.sqft >= 500 && x.num_of_beds >=2 && x.num_of_baths >= 2.5);

... dan agar Anda dapat membacanya dengan lebih mudah:

const filteredHomes = json.homes.filter( x => 
  x.price <= 1000 && 
  x.sqft >= 500 && 
  x.num_of_beds >=2 && 
  x.num_of_baths >= 2.5
);

2
mungkin karena itu sama dengan jawaban CMS hanya dengan fungsi panah
Erich

11

di sini adalah biola yang berfungsi dengan baik di IE8 menggunakan fungsi jquery MAP

http://jsfiddle.net/533135/Cj4j7/

json.HOMES = $.map(json.HOMES, function(val, key) {
    if (Number(val.price) <= 1000
            && Number(val.sqft) >= 500
            && Number(val.num_of_beds) >=2
            && Number(val.num_of_baths ) >= 2.5)
        return val;
});

7

Anda dapat menggunakan jQuery.grep () karena jQuery 1.0:

$.grep(homes, function (h) {
  return h.price <= 1000
    && h.sqft >= 500
    && h.num_of_beds >= 2
    && h.num_of_baths >= 2.5
});

7

Anda dapat melakukan ini dengan cukup mudah - mungkin ada banyak implementasi yang dapat Anda pilih, tetapi ini adalah ide dasar saya (dan mungkin ada beberapa format di mana Anda dapat beralih pada objek dengan jQuery, saya tidak bisa memikirkannya sekarang):

function filter(collection, predicate)
{
    var result = new Array();
    var length = collection.length;

    for(var j = 0; j < length; j++)
    {
        if(predicate(collection[j]) == true)
        {
             result.push(collection[j]);
        }
    }

    return result;
}

Dan kemudian Anda bisa menjalankan fungsi ini seperti:

filter(json, function(element)
{
    if(element.price <= 1000 && element.sqft >= 500 && element.num_of_beds > 2 && element.num_of_baths > 2.5)
        return true;

    return false;
});

Dengan cara ini, Anda dapat menjalankan filter berdasarkan predikat apa pun yang Anda tetapkan, atau bahkan memfilter beberapa kali menggunakan filter yang lebih kecil.


Ubah "panjang int" menjadi "panjang var" dalam cuplikan pertama
Bahasa Melayu Desai

4

Anda dapat menerapkan metode filter sendiri yang memenuhi kebutuhan Anda, berikut ini caranya:

function myfilter(array, test){
    var passedTest =[];
    for (var i = 0; i < array.length; i++) {
       if(test( array[i]))
          passedTest.push(array[i]);
    }

    return passedTest;
}

var passedHomes = myfilter(homes,function(currentHome){
     return ((currentHome.price <= 1000 )&& (currentHome.sqft >= 500 )&&(currentHome.num_of_beds >=2 )&&(currentHome.num_of_baths >= 2.5));
});

Semoga bermanfaat!


Kondisi predikat saya berubah dari berbagai onclick. Bagaimana cara menyimpan nilai predikat saat ini dan menerapkannya ke fungsi myfilter kapan pun saya mau?
Desai Melayu

3

Anda harus memeriksa OGX.List yang memiliki metode penyaringan bawaan dan memperluas javascript array standar (dan juga pengelompokan, pengurutan dan pencarian). Berikut daftar operator yang didukung untuk filter:

'eq' //Equal to
'eqjson' //For deep objects, JSON comparison, equal to
'neq' //Not equal to
'in' //Contains
'nin' //Doesn't contain
'lt' //Lesser than
'lte' //Lesser or equal to
'gt' //Greater than
'gte' //Greater or equal to
'btw' //Between, expects value to be array [_from_, _to_]
'substr' //Substring mode, equal to, expects value to be array [_from_, _to_, _niddle_]
'regex' //Regex match

Anda bisa menggunakannya dengan cara ini

  let list = new OGX.List(your_array);
  list.addFilter('price', 'btw', 100, 500);
  list.addFilter('sqft', 'gte', 500);
  let filtered_list = list.filter();

Atau bahkan dengan cara ini

  let list = new OGX.List(your_array);
  let filtered_list = list.get({price:{btw:[100,500]}, sqft:{gte:500}});

Atau sebagai satu liner

   let filtered_list = new OGX.List(your_array).get({price:{btw:[100,500]}, sqft:{gte:500}});

2

Atau Anda cukup menggunakan $.each(yang juga berfungsi untuk objek, tidak hanya array) dan membangun array baru seperti:

var json = {
    'homes': [{
            "home_id": "1",
            "price": "925",
            "sqft": "1100",
            "num_of_beds": "2",
            "num_of_baths": "2.0",
        }, {
            "home_id": "2",
            "price": "1425",
            "sqft": "1900",
            "num_of_beds": "4",
            "num_of_baths": "2.5",
        },
        // ... (more homes) ...     
        {
            "home_id": "3-will-be-matched",
            "price": "925",
            "sqft": "1000",
            "num_of_beds": "2",
            "num_of_baths": "2.5",
        },
    ]
}

var homes = [];
$.each(json.homes, function(){
    if (this.price <= 1000
        && this.sqft >= 500
        && this.num_of_beds >= 2
        && this.num_of_baths >= 2.5
    ) {
        homes.push(this);
    }
});

2

Saya menggunakan ruleOutfungsi saya untuk memfilter objek berdasarkan nilai properti spesifik yang tidak diinginkan. Saya memahami bahwa dalam contoh Anda, Anda ingin menggunakan kondisi alih-alih nilai, tetapi jawaban saya valid untuk judul pertanyaan, jadi saya ingin meninggalkan metode saya di sini.

function ruleOut(arr, filterObj, applyAllFilters=true) {    
    return arr.filter( row => {            
        for (var field in filterObj) {
            var val = row[field];
            if (val) {                    
                if (applyAllFilters && filterObj[field].indexOf(val) > -1) return false;                
                else if (!applyAllFilters) {                        
                    return filterObj[field].filter(function(filterValue){ 
                        return (val.indexOf(filterValue)>-1);
                    }).length == 0;                 
                }
            }
        }
        return true;
    });
}

Katakanlah Anda memiliki daftar aktor seperti ini:

let actors = [
  {userName:"Mary", job:"star", language:"Turkish"},
  {userName:"John", job:"actor", language:"Turkish"},
  {userName:"Takis", job:"star", language:"Greek"},
  {userName:"Joe", job:"star", language:"Turkish"},
  {userName:"Bill", job:"star", language:"Turkish"}
];

dan Anda ingin menemukan semua aktor yang dinilai sebagai bintang Holywood, kewarganegaraan mereka tidak boleh salah satu dari 'Inggris', 'Italia', 'Spanyol', 'Yunani', ditambah nama mereka tidak akan menjadi salah satu dari 'Mary', 'Joe'. Contoh Bizzar, saya tahu! Lagi pula, dengan serangkaian kondisi tersebut Anda akan membuat objek berikut:

let unwantedFieldsFilter= { 
  userName: ['Mary', 'Joe'],    
  job: ['actor'],   
  language: ['English', 'Italian', 'Spanish', 'Greek']  
};

OK, sekarang jika Anda ruleOut(actors, unwantedFieldsFilter)hanya akan mendapatkannya

[{userName: "Bill", job: "star", bahasa: "Turkish"}]

Dan Bill adalah laki-laki Anda, karena namanya bukan salah satu dari 'Mary', 'Joe', kewarganegaraannya tidak termasuk dalam ['Inggris', 'Italia', 'Spanyol', 'Yunani'] plus ia seorang Bintang!

Ada satu opsi dalam metode saya, yaitu applyAllFiltersdan benar secara default. Jika Anda akan mencoba untuk ruleOut dengan param ini diset sebagai false, itu akan berfungsi sebagai pemfilteran 'ATAU' dan bukannya 'DAN'. Contoh: ruleOut(actors, {job:["actor"], language:["Italian"]}, false)akan membuat Anda semua orang yang bukan aktor atau Italia:

[{userName: "Mary", pekerjaan: "bintang", bahasa: "Turki"},
{userName: "Takis", pekerjaan: "bintang", bahasa: "Yunani"},
{userName: "Joe", pekerjaan: "bintang", bahasa: "Turki"},
{userName: "Bill", pekerjaan: "bintang", bahasa: "Turki"}]


1
var filterHome = homes.filter(home =>
  return (home.price <= 999 &&
         home.num_of_baths >= 2.5 &&
         home.num_of_beds >=2 &&
         home.sqft >= 998));
console.log(filterHome);

Anda dapat menggunakan fungsi lambda ini. Lebih detail dapat ditemukan di sini karena kami memfilter data berdasarkan Anda memiliki kondisi yang mengembalikan benar atau salah dan itu akan mengumpulkan data dalam array yang berbeda sehingga array Anda yang sebenarnya tidak akan dimodifikasi.

@ JGreig Silakan lihat.


1
const x = JSON.stringify(data);
const y = 'search text';

const data = x.filter(res => {
        return(JSON.stringify(res).toLocaleLowerCase()).match(x.toLocaleLowerCase());
      });

0
const state.contactList = [{
    name: 'jane',
    email: 'jane@gmail.com'
  },{},{},...]

const fileredArray = state.contactsList.filter((contactItem) => {
  const regex = new RegExp(`${action.payload}`, 'gi');
  return contactItem.nameProperty.match(regex) || 
    contactItem.emailProperty.match(regex);
});


// contactList: all the contacts stored in state
// action.payload: whatever typed in search field
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.