Câu hỏi JavaScript: filter () cho các đối tượng


ECMAScript 5 có filter() nguyên mẫu cho Array các loại, nhưng không Object các loại, nếu tôi hiểu chính xác.

Tôi sẽ triển khai như thế nào filter() cho Objects trong JavaScript?

Giả sử tôi có đối tượng này:

var foo = {
    bar: "Yes"
};

Và tôi muốn viết một filter() hoạt động trên ObjectS:

Object.prototype.filter = function(predicate) {
    var result = {};

    for (key in this) {
        if (this.hasOwnProperty(key) && !predicate(this[key])) {
            result[key] = this[key];
        }
    }

    return result;
};

Điều này làm việc khi tôi sử dụng nó trong bản demo sau, nhưng khi tôi thêm nó vào trang web của tôi sử dụng jQuery 1.5 và jQuery UI 1.8.9, tôi nhận được các lỗi JavaScript trong FireBug.

Object.prototype.filter = function(predicate) {
  var result = {};
  for (key in this) {
    if (this.hasOwnProperty(key) && !predicate(this[key])) {
      console.log("copying");
      result[key] = this[key];
    }
  }
  return result;
};

var foo = {
  bar: "Yes",
  moo: undefined
};

foo = foo.filter(function(property) {
  return typeof property === "undefined";
});

document.getElementById('disp').innerHTML = JSON.stringify(foo, undefined, '  ');
console.log(foo);
#disp {
  white-space: pre;
  font-family: monospace
}
<div id="disp"></div>


75
2018-02-21 22:42


gốc


Lỗi nào bạn nhận được, cụ thể? - NT3RP
Các lỗi bạn đang gặp phải là gì? Đăng chúng nếu có thể :) - Zack The Human
Có một chút lịch sử không rõ ràng về jQuery và các kịch bản mở rộng Object.prototype: bugs.jquery.com/ticket/2721 - Crescent Fresh


Các câu trả lời:


Không bao giờ mở rộng Object.prototype.

Những điều khủng khiếp sẽ xảy ra với mã của bạn. Mọi thứ sẽ phá vỡ. Bạn đang mở rộng tất cả các các kiểu đối tượng, bao gồm cả các đối tượng literals.

Dưới đây là ví dụ nhanh bạn có thể thử:

    // Extend Object.prototype
Object.prototype.extended = "I'm everywhere!";

    // See the result
alert( {}.extended );          // "I'm everywhere!"
alert( [].extended );          // "I'm everywhere!"
alert( new Date().extended );  // "I'm everywhere!"
alert( 3..extended );          // "I'm everywhere!"
alert( true.extended );        // "I'm everywhere!"
alert( "here?".extended );     // "I'm everywhere!"

Thay vào đó, hãy tạo một hàm mà bạn truyền đối tượng.

Object.filter = function( obj, predicate) {
    var result = {}, key;
    // ---------------^---- as noted by @CMS, 
    //      always declare variables with the "var" keyword

    for (key in obj) {
        if (obj.hasOwnProperty(key) && !predicate(obj[key])) {
            result[key] = obj[key];
        }
    }

    return result;
};

111
2018-02-21 22:43



Không thực sự là một câu trả lời, điều này thực sự phải là một bình luận. Nhưng upvoted anyway vì nó là chính xác. - Dykam
@patrick: cung cấp cho một người đàn ông một bánh mì và bạn sẽ nuôi anh ta trong một ngày, dạy anh ta làm thế nào để nướng và bạn sẽ nuôi anh ta cho một đời (hoặc một cái gì đó, tôi danish, tôi không biết những câu nói tiếng Anh chính xác ;) - Martin Jespersen
Bạn đang làm sai rồi ...! Predicate (obj [key]) nên là vị ngữ (obj [key]) - pyrotechnick
@patrick dw: Không. Trước tiên, tôi không đề cập đến việc mở rộng / không mở rộng các nguyên mẫu. Thứ hai, developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… - "Tạo một mảng mới với tất cả các phần tử vượt qua bài kiểm tra được thực hiện bởi hàm được cung cấp". Thực hiện chính xác đối diện trên toàn cầu có vẻ khá ngớ ngẩn, phải không? - pyrotechnick
@pyrotechnick: Đúng vậy, bạn không đề cập đến việc mở rộng / không mở rộng nguyên mẫu, và đó là quan điểm của tôi. Bạn nói tôi đang làm điều đó sai, nhưng chỉ "nó" tôi đang làm là nói OP không mở rộng Object.prototype. Từ câu hỏi: "Thao tác này ... nhưng khi tôi thêm nó vào trang web của mình ..., tôi gặp phải lỗi JavaScript" Nếu OP quyết định thực hiện .filter() với hành vi ngược lại của Array.prototpe.filter, điều đó tùy thuộc vào anh ta / cô ấy. Xin vui lòng để lại một bình luận dưới câu hỏi nếu bạn muốn thông báo cho OP rằng mã là sai, nhưng đừng nói với tôi rằng Tôi là làm sai khi nó không phải là mã của tôi. - user113716


Đầu tiên, nó được coi là thực hành xấu để mở rộng Object.prototype. Thay vào đó, hãy cung cấp tính năng của bạn làm chức năng tiện ích trên Object, giống như đã có Object.keys, Object.assign, Object.is, ... v.v.

Bạn có thể dùng reduce và Object.keys để triển khai bộ lọc mong muốn (sử dụng ES6 cú pháp mũi tên):

Object.filter = (obj, predicate) => 
    Object.keys(obj)
          .filter( key => predicate(obj[key]) )
          .reduce( (res, key) => (res[key] = obj[key], res), {} );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

Lưu ý rằng trong đoạn mã trên predicate phải là một sự bao gồm điều kiện (trái với loại trừ điều kiện OP được sử dụng), để nó phù hợp với cách thức Array.prototype.filter công trinh.

Với Object.assign

Trong giải pháp trên, toán tử dấu phẩy được sử dụng trong reduce một phần để trả lại đột biến res vật. Điều này tất nhiên có thể được viết dưới dạng hai câu lệnh thay vì một biểu thức, nhưng câu sau thì ngắn gọn hơn. Để làm điều đó mà không có toán tử dấu phẩy, bạn có thể sử dụng Object.assign thay vào đó, cái nào làm trả về đối tượng bị biến đổi:

Object.filter = (obj, predicate) => 
    Object.keys(obj)
          .filter( key => predicate(obj[key]) )
          .reduce( (res, key) => Object.assign(res, { [key]: obj[key] }), {} );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

Sử dụng cú pháp spread

Ở đây chúng tôi di chuyển Object.assign gọi ra khỏi vòng lặp, do đó, nó chỉ được thực hiện một lần, và vượt qua nó các khóa riêng lẻ như các đối số riêng biệt (sử dụng cú pháp truyền bá):

Object.filter = (obj, predicate) => 
    Object.assign(...Object.keys(obj)
                    .filter( key => predicate(obj[key]) )
                    .map( key => ({ [key]: obj[key] }) ) );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

Chức năng tách: tùy chỉnh Object.from

Khi giải pháp dịch đối tượng thành mảng trung gian và sau đó chuyển đổi lại thành đối tượng đơn giản, sẽ hữu ích khi sử dụng Object.entries (ES2017) và để tạo ra một phương thức ngược lại (tức là tạo một đối tượng từ một mảng các cặp khóa / giá trị).

Nó dẫn đến hai phương pháp "một lớp" này Object:

Object.from = arr => Object.assign(...arr.map( ([k, v]) => ({[k]: v}) ));
Object.filter = (obj, predicate) => Object.from(Object.entries(obj).filter(predicate));

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};

var filtered = Object.filter(scores, ([name, score]) => score > 1); 
console.log(filtered);

Hàm vị ngữ nhận cặp khóa / giá trị làm đối số ở đây, có một chút khác biệt, nhưng cho phép nhiều khả năng hơn trong logic của hàm vị ngữ.


124
2018-06-03 13:46



Nó có thể là truy vấn phức tạp hơn? Ví dụ: x => x.Expression.Filters.Filter - IamStalker
@IamStalker, bạn đã thử chưa? Nó không quan trọng, miễn là bạn cung cấp một hàm hợp lệ trong đối số thứ hai. NB: Tôi không biết .Filter là ở cuối, nhưng nếu nó là một hàm, bạn cần gọi nó ( x => x.Expression.Filters.Filter() ) - trincot
quá lâu! có thể được thực hiện trên 1 dòng. kiểm tra điều này - Abdennour TOUMI
Các tính năng mới hơn có thể tạo ra ít mã hơn, nhưng chúng cũng làm cho hiệu suất chậm hơn. Mã tuân thủ Ed 3 chạy nhanh gấp hai lần trong hầu hết các trình duyệt. - RobG
Đây là câu trả lời hay nhất. - Sheljohn


Nếu bạn sẵn sàng sử dụng gạch dưới hoặc là lodash, bạn có thể dùng pick (hoặc ngược lại, omit).

Ví dụ từ tài liệu của underscore:

_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
// {name: 'moe', age: 50}

Hoặc với một cuộc gọi lại (cho lodash, sử dụng pickBy):

_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
  return _.isNumber(value);
});
// {age: 50}

14
2017-10-05 11:40



Trong trường hợp của lodash bạn cần sử dụng _.pickBy cho trường hợp thứ hai - Attila Miklosi
cảm ơn @AttilaMiklosim, tôi đã cập nhật câu trả lời - Bogdan D
lodash là một giải pháp tồi vì việc lọc đối tượng trống cũng sẽ xóa số. - mibbit


Như patrick đã tuyên bố đây là một ý tưởng tồi, vì nó gần như chắc chắn sẽ phá vỡ bất kỳ mã bên thứ 3 nào bạn có thể muốn sử dụng.

Tất cả các thư viện như jquery hoặc prototype sẽ phá vỡ nếu bạn mở rộng Object.prototype, lý do là lặp đi lặp lại lười biếng trên các đối tượng (không có hasOwnProperty kiểm tra) sẽ phá vỡ vì các hàm bạn thêm sẽ là một phần của phép lặp.


2
2018-02-21 22:47



upvoted cho giải thích 'tại sao' của điều này là một ý tưởng tồi rõ ràng và ngắn gọn. - Michael Liquori


Tôi đã tạo ra Object.filter() không chỉ lọc theo hàm mà còn chấp nhận một dãy các khóa để bao gồm. Tham số thứ ba tùy chọn sẽ cho phép bạn đảo ngược bộ lọc.

Được:

var foo = {
    x: 1,
    y: 0,
    z: -1,
    a: 'Hello',
    b: 'World'
}

Mảng:

Object.filter(foo, ['z', 'a', 'b'], true);

Chức năng:

Object.filter(foo, function (key, value) {
    return Ext.isString(value);
});

Mã số

Tuyên bố từ chối trách nhiệm: Tôi đã chọn sử dụng lõi Ext JS cho ngắn gọn. Không cảm thấy nó là cần thiết để viết checkers loại cho các loại đối tượng vì nó không phải là một phần của câu hỏi.


1
2017-12-31 17:31





Làm thế nào về:

function filterObj(keys, obj) {
  const newObj = {};
  for (let key in obj) {
    if (keys.includes(key)) {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

Hoặc là...

function filterObj(keys, obj) {
  const newObj = {};
  Object.keys(obj).forEach(key => {
    if (keys.includes(key)) {
      newObj[key] = obj[key];
    }
  });
  return newObj;
}

0
2018-05-20 17:33





Được

object = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};

keys = ['firstname', 'age'];

sau đó :

keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});
// {firstname:'abd', age: 16}

// Helper
function filter(object, ...keys) {
  return keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});
  
};

//Example
const person = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};

// Expected to pick only firstname and age keys
console.log(
  filter(person, 'firstname', 'age')
)


0
2017-11-12 11:17



Điều này không sử dụng một hàm vị ngữ đã cho như được yêu cầu bởi câu hỏi. - trincot