Làm thế nào để lọc mảng đối tượng dựa trên các thuộc tính?


473

Tôi có mảng JavaScript sau đây của các đối tượng nhà bất động sản:

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;

Những gì tôi muốn làm là có thể thực hiện một bộ lọc trên đối tượng để trả về một tập hợp con của các đối tượng "nhà".

Ví dụ, tôi muốn để có thể lọc dựa trên: price, sqft, num_of_beds, và num_of_baths.

Làm cách nào tôi có thể thực hiện một cái gì đó trong JavaScript như mã giả bên dưới:

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

Lưu ý, cú pháp không nhất thiết phải chính xác như trên. Đây chỉ là một ví dụ.


7
Điều này dường như gần giống với stackoverflow.com/questions/1694717/ từ
Lưỡi liềm tươi

3
var json = { ... }JSON là một ký hiệu văn bản để trao đổi dữ liệu. (Thêm tại đây.) Nếu bạn đang xử lý mã nguồn JavaScript và không xử lý chuỗi , bạn sẽ không xử lý JSON.
TJ Crowder

1
Đừng dùng eval. Đó là thực tế xấu và có thể gây ra vấn đề hiệu suất. Chúng tôi phải loại bỏ một loạt những người trong một dự án vì bộ xử lý đã bị khóa.
SDH

Câu trả lời:


718

Bạn có thể sử dụng Array.prototype.filterphương pháp:

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

Ví dụ trực tiếp:

Phương pháp này là một phần của tiêu chuẩn ECMAScript 5 Edition mới và có thể được tìm thấy trên hầu hết các trình duyệt hiện đại.

Đối với IE, bạn có thể bao gồm các phương pháp sau để tương thích:

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
/ *, thisp * / là gì?
JGreig

2
@JGreig: Chỉ là một nhận xét để chỉ ra rằng một đối số tùy chọn có thể được thông qua, đối số không được chỉ định trực tiếp vì tiêu chuẩn ECMA nói chính xác rằng phương thức này chỉ mong đợi một đối số ( Array.prototype.filter.length == 1;). Khi bạn sử dụng đối số thứ hai, nó sẽ được sử dụng làm thisgiá trị bên trong hàm gọi lại.
CMS

1
@CMS, bạn có chắc mã này hoạt động không? Điều này đang trả về một mảng trống, ngay cả khi tôi đặt giá thực sự cao và sqft / giường / phòng tắm thực sự thấp. Bạn có chắc mã này hoạt động?
JGreig

2
@JGreig: Vâng, nó hoạt động, bạn nên kiểm tra tiêu chí của mình, có thể bạn có thể đăng một ví dụ đầy đủ hơn về JSON của bạn trong pastie.org hoặc jsbin.com và các tiêu chí bạn đang sử dụng để lọc, để tôi có thể giúp bạn tốt hơn.
CMS

2
@CMS Sẽ tốt hơn nếu câu trả lời bao gồm thực tế là một mảng mới được trả về, mảng ban đầu không được sửa đổi. Mặc dù điều này được nói trong liên kết được cung cấp, tôi tìm thấy câu trả lời mà không có điều này là không đầy đủ hoặc không chính xác
GWorking

29

Bạn có thể thử sử dụng khung như jLinq - sau đây là mẫu mã sử dụng jLinq

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

Để biết thêm thông tin, bạn có thể theo liên kết http://www.hugoware.net/projects/jlinq


26

Tôi thích khung Underscore. Nó cho thấy nhiều hoạt động hữu ích với các đối tượng. Nhiệm vụ của bạn:

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

có thể được ghi đè như:

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

Hy vọng nó sẽ hữu ích cho bạn!


Làm thế nào để điều này gạch dưới hoạt động và chính xác nó có nghĩa là gì?
John Demetriou

4
Mới được phát hiện underscore-query( github.com/davidgtonge/underscore-query ) sử dụng cú pháp giống MongoDB để truy vấn các mảng javascript. Vì vậy, ở đây bạn sử dụng_.query(homes, {price: {$lt:1000}, sqft: {$gte: 500}, num_of_beds: {$gte:2}, num_of_baths: {$gte: 2.5}}
nguyên mẫu

12

Tôi ngạc nhiên không ai đã đăng phản hồi một dòng:

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

... Và chỉ để bạn có thể đọc nó dễ dàng hơn:

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

2
có lẽ bởi vì nó giống như câu trả lời của CMS chỉ với các chức năng mũi tên
Erich

11

Đây là fiddle hoạt động tốt trong IE8 bằng cách sử dụng chức năng MAP jquery

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

Bạn có thể sử dụng jQuery.grep () kể từ 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

Bạn có thể thực hiện việc này khá dễ dàng - có thể có nhiều triển khai bạn có thể chọn, nhưng đây là ý tưởng cơ bản của tôi (và có thể có một số định dạng mà bạn có thể lặp qua một đối tượng với jQuery, tôi không thể nghĩ về nó ngay bây giờ):

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

Và sau đó bạn có thể gọi hàm này như vậy:

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

Bằng cách này, bạn có thể gọi bộ lọc dựa trên bất kỳ vị từ nào bạn xác định hoặc thậm chí lọc nhiều lần bằng các bộ lọc nhỏ hơn.


Vui lòng thay đổi "int length" thành "var length" trong đoạn trích đầu tiên
Malay Desai

4

Bạn có thể tự thực hiện một phương pháp lọc đáp ứng nhu cầu của mình, đây là cách thực hiện:

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

Hy vọng nó giúp!


Tình trạng vị ngữ của tôi thay đổi từ các onclicks khác nhau. Làm thế nào có thể lưu trữ các giá trị vị ngữ hiện tại và áp dụng nó cho hàm myfilter bất cứ khi nào tôi muốn?
Malay Desai

3

Bạn nên kiểm tra OGX.List đã xây dựng các phương thức lọc và mở rộng mảng javascript tiêu chuẩn (và cũng nhóm, sắp xếp và tìm kiếm). Dưới đây là danh sách các toán tử mà nó hỗ trợ cho các bộ lọc:

'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

Bạn có thể sử dụng nó theo cách này

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

Hoặc thậm chí theo cách này

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

Hoặc như một lớp lót

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

2

Hoặc bạn có thể chỉ cần sử dụng $.each(cũng hoạt động cho các đối tượng, không chỉ mảng) và xây dựng một mảng mới như vậy:

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

Tôi sử dụng ruleOutchức năng của mình để lọc các đối tượng dựa trên các giá trị thuộc tính không mong muốn cụ thể. Tôi hiểu rằng trong ví dụ của bạn, bạn muốn sử dụng các điều kiện thay vì các giá trị, nhưng câu trả lời của tôi là hợp lệ cho tiêu đề câu hỏi, vì vậy tôi muốn để phương thức của mình ở đây.

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

Giả sử bạn có một danh sách các diễn viên như thế này:

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

và bạn muốn tìm tất cả các diễn viên được đánh giá là ngôi sao Holywood, quốc tịch của họ không nên là một trong những 'người Anh', 'người Ý', 'người Tây Ban Nha', 'người Hy Lạp', cộng với tên của họ sẽ không phải là một trong 'Mary', "Joe". Ví dụ Bizzar, tôi biết! Dù sao, với tập hợp các điều kiện đó, bạn sẽ tạo đối tượng sau:

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

Được rồi, bây giờ nếu bạn ruleOut(actors, unwantedFieldsFilter)chỉ nhận được

[{userName: "Bill", công việc: "sao", ngôn ngữ: "Thổ Nhĩ Kỳ"}]

Và Bill là người đàn ông của bạn, vì tên của anh ấy không phải là một trong 'Mary', 'Joe', quốc tịch của anh ấy không được bao gồm trong ['tiếng Anh', 'tiếng Ý', 'tiếng Tây Ban Nha', 'tiếng Hy Lạp'] cộng với anh ấy là một ngôi sao!

Có một tùy chọn trong phương thức của tôi, đó là applyAllFiltersvà đúng theo mặc định. Nếu bạn cố gắng quy tắcOut với thông số này được đặt thành false, thì nó sẽ hoạt động như một bộ lọc 'HOẶC' thay vì 'VÀ'. Ví dụ: ruleOut(actors, {job:["actor"], language:["Italian"]}, false)sẽ giúp bạn có được mọi người không phải là diễn viên hay người Ý:

[{userName: "Mary", công việc: "ngôi sao", ngôn ngữ: "Thổ Nhĩ Kỳ"},
{userName: "Takis", công việc: "ngôi sao", ngôn ngữ: "Hy Lạp"},
{userName: "Joe", công việc: "ngôi sao", ngôn ngữ: "Tiếng Thổ Nhĩ Kỳ"},
{userName: "Bill", công việc: "ngôi sao", ngôn ngữ: "Tiếng Thổ Nhĩ Kỳ"}]


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

Bạn có thể sử dụng chức năng lambda này. Chi tiết hơn có thể được tìm thấy ở đây vì chúng tôi đang lọc dữ liệu dựa trên điều kiện bạn trả về đúng hoặc sai và nó sẽ thu thập dữ liệu trong các mảng khác nhau để mảng thực tế của bạn sẽ không bị sửa đổi.

@JGreig Hãy nhìn vào nó.


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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.