web-dev-qa-db-ja.com

JavaScript:オブジェクトのfilter()

ECMAScript 5には、Array型のfilter()プロトタイプがありますが、正しく理解できれば、Object型ではありません。

JavaScriptでObjectsにfilter()を実装するにはどうすればよいですか?

このオブジェクトがあるとしましょう:

var foo = {
    bar: "Yes"
};

そして、Objectsで動作するfilter()を書きたい:

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

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

    return result;
};

次のデモで使用すると機能しますが、jQuery 1.5とjQuery UI 1.8.9を使用するサイトに追加すると、FireBugでJavaScriptエラーが発生します。

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>

Object.prototypeを決して拡張しないでください。

コードに恐ろしいことが起こります。物事は壊れます。オブジェクトリテラルを含むallオブジェクトタイプを拡張しています。

以下に簡単な例を示します:

    // 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!"

代わりに、オブジェクトを渡す関数を作成します。

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

まず、 Object.prototypeを拡張することは悪い習慣と見なされます 。代わりに、すでにObject.keysObject.assignObject.isなどがあるように、Objectのユーティリティ関数として機能を提供します。

ここでいくつかのソリューションを提供します。

  1. reduceおよびObject.keysの使用
  2. (1)として、Object.assignと組み合わせて
  3. mapの代わりにreduceとspread構文を使用する
  4. Object.entriesおよびObject.fromEntriesの使用

1. reduceおよびObject.keysの使用

reduce および Object.keys を使用して、目的のフィルターを実装します(ES6を使用 矢印構文 ):

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

上記のコードではpredicateinclusion条件(exclusion使用するOPの条件)、 Array.prototype.filter の動作方法と一致するようにします。

2.(1)として、Object.assignと組み合わせて

上記のソリューションでは、reduce部分で カンマ演算子 を使用して、変更されたresオブジェクトを返します。もちろん、これは1つの式ではなく2つのステートメントとして記述できますが、後者はより簡潔です。コンマ演算子なしでそれを行うには、代わりに Object.assign を使用できます。これは、doesが変更されたオブジェクトを返します。

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

3. mapの代わりにreduceとspread構文を使用する

ここでは、ループからObject.assign呼び出しを移動します。したがって、呼び出しは1回だけで、個別のキーを個別の引数として渡します( spread syntax を使用)。

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

4. Object.entriesおよびObject.fromEntriesの使用

ソリューションはオブジェクトを中間配列に変換し、それをプレーンオブジェクトに変換するため、 Object.entries (ES2017)を使用し、反対の方法(つまり、 キー/値のペアの配列からオブジェクトを作成 )。執筆時点で Object.fromEntries提案 はステージ3にあります。Firefoxと、最近ではChrome have implemented it です。それ以外の場合は、ポリフィルを使用できます。

Object(ポリフィルを含む)でこれらの2つの「ワンライナー」メソッドにつながります。

Object.fromEntries = arr => Object.assign({}, ...arr.map( ([k, v]) => ({[k]: v}) ));
Object.filter = (obj, predicate) => 
                  Object.fromEntries(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);

述語関数はここで引数としてキー/値のペアを取得しますが、これは少し異なりますが、述語関数のロジックでより多くの可能性を可能にします。

184
trincot

アンダースコアまたはlodashを使用する場合は、使用できます pick (またはその反対の omit )。

アンダースコアのドキュメントの例:

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

または、コールバック(lodashの場合、 pickBy )を使用します。

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

パトリックがすでに述べたように、これはあなたがこれまでに使用したいサードパーティのコードをほぼ確実に破壊するため、これは悪い考えです。

Object.prototypeを拡張すると、jqueryやprototypeなどのすべてのライブラリが破損します。理由は、追加する関数が反復の一部になるため、オブジェクトに対する遅延反復(hasOwnPropertyチェックなし)が破損するためです。

5

ES6アプローチ...

以下にこのオブジェクトがあると想像してください:

const developers = {
  1: {
   id: 1,
   name: "Brendan", 
   family: "Eich"
  },
  2: {
   id: 2,
   name: "John", 
   family: "Resig"
  },  
  3: {
   id: 3,
   name: "Alireza", 
   family: "Dezfoolian"
 }
};

関数を作成します。

const filterObject = (obj, filter, filterValue) => 
   Object.keys(obj).reduce((acc, val) => 
   (obj[val][filter] === filterValue ? acc : {
       ...acc,
       [val]: obj[val]
   }                                        
), {});

そしてそれを呼び出す:

filterObject(developers, "name", "Alireza");

そしてreturn

{
  1: {
  id: 1,
  name: "Brendan", 
  family: "Eich"
  },
  2: {
   id: 2,
   name: "John", 
   family: "Resig"
  }
}
4
Alireza

関数でフィルタリングするだけでなく、含めるキーの配列を受け入れるObject.filter()を作成しました。オプションの3番目のパラメーターを使用すると、フィルターを反転できます。

与えられた:

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

アレイ:

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

関数:

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

コード

免責事項:簡潔にするためにExt JSコアを使用することにしました。質問の一部ではないため、オブジェクト型の型チェッカーを記述する必要があるとは感じませんでした。

// Helper function
function print(obj) {
    document.getElementById('disp').innerHTML += JSON.stringify(obj, undefined, '  ') + '<br />';
    console.log(obj);
}

Object.filter = function (obj, ignore, invert) {
    let result = {}; // Returns a filtered copy of the original list
    if (ignore === undefined) {
        return obj;   
    }
    invert = invert || false;
    let not = function(condition, yes) { return yes ? !condition : condition; };
    let isArray = Ext.isArray(ignore);
    for (var key in obj) {
        if (obj.hasOwnProperty(key) &&
                !(isArray && not(!Ext.Array.contains(ignore, key), invert)) &&
                !(!isArray && not(!ignore.call(undefined, key, obj[key]), invert))) {
            result[key] = obj[key];
        }
    }
    return result;
};

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

print(Object.filter(foo, ['z', 'a', 'b'], true));
print(Object.filter(foo, (key, value) => Ext.isString(value)));
#disp {
    white-space: pre;
    font-family: monospace
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/extjs/4.2.1/builds/ext-core.min.js"></script>
<div id="disp"></div>
3
Mr. Polywhirl

与えられた

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

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

その後:

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')
)
2
Abdennour TOUMI

どうですか:

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

または...

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

誰もが言ったように、プロトタイプに手を出さないでください。代わりに、そうするための関数を書くだけです。 lodashを使用した私のバージョンは次のとおりです。

import each from 'lodash/each';
import get from 'lodash/get';

const myFilteredResults = results => {
  const filteredResults = [];

  each(results, obj => {
    // filter by whatever logic you want.

    // sample example
    const someBoolean = get(obj, 'some_boolean', '');

    if (someBoolean) {
      filteredResults.Push(obj);
    }
  });

  return filteredResults;
};
0
saran3h

これらの場合、オブジェクトを処理できるjquery $ .mapを使用します。他の回答で述べたように、ネイティブプロトタイプを変更することはお勧めできません( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes

以下は、オブジェクトのプロパティをチェックするだけでフィルタリングする例です。条件が真の場合は独自のオブジェクトを返し、そうでない場合はundefinedを返します。 undefinedプロパティは、そのレコードをオブジェクトリストから削除します。

$.map(yourObject, (el, index)=>{
    return el.yourProperty ? el : undefined;
});
0
Marcel Kohls

私の考え抜いた解決策:

function objFilter(obj, filter, nonstrict){
  r = {}
  if (!filter) return {}
  if (typeof filter == 'string') return {[filter]: obj[filter]}
  for (p in obj) {
    if (typeof filter == 'object' &&  nonstrict && obj[p] ==  filter[p]) r[p] = obj[p]
    else if (typeof filter == 'object' && !nonstrict && obj[p] === filter[p]) r[p] = obj[p]
    else if (typeof filter == 'function'){ if (filter(obj[p],p,obj)) r[p] = obj[p]}
    else if (filter.length && filter.includes(p)) r[p] = obj[p]
  }
  return r
}

テストケース:

obj = {a:1, b:2, c:3}

objFilter(obj, 'a') // returns: {a: 1}
objFilter(obj, ['a','b']) // returns: {a: 1, b: 2}
objFilter(obj, {a:1}) // returns: {a: 1}
objFilter(obj, {'a':'1'}, true) // returns: {a: 1}
objFilter(obj, (v,k,o) => v%2===1) // returns: {a: 1, c: 3}

https://Gist.github.com/khullah/872d5a174108823159d845cc5baba337

0
Z. Khullah