ECMAScript 5には、Array
型のfilter()
プロトタイプがありますが、正しく理解できれば、Object
型ではありません。
JavaScriptでObject
sにfilter()
を実装するにはどうすればよいですか?
このオブジェクトがあるとしましょう:
var foo = {
bar: "Yes"
};
そして、Object
sで動作する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;
};
まず、 Object.prototype
を拡張することは悪い習慣と見なされます 。代わりに、すでにObject.keys
、Object.assign
、Object.is
などがあるように、Object
のユーティリティ関数として機能を提供します。
ここでいくつかのソリューションを提供します。
reduce
およびObject.keys
の使用Object.assign
と組み合わせてmap
の代わりにreduce
とspread構文を使用するObject.entries
およびObject.fromEntries
の使用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);
上記のコードではpredicate
はinclusion条件(exclusion使用するOPの条件)、 Array.prototype.filter
の動作方法と一致するようにします。
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);
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);
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);
述語関数はここで引数としてキー/値のペアを取得しますが、これは少し異なりますが、述語関数のロジックでより多くの可能性を可能にします。
アンダースコアまたは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}
パトリックがすでに述べたように、これはあなたがこれまでに使用したいサードパーティのコードをほぼ確実に破壊するため、これは悪い考えです。
Object.prototype
を拡張すると、jqueryやprototypeなどのすべてのライブラリが破損します。理由は、追加する関数が反復の一部になるため、オブジェクトに対する遅延反復(hasOwnProperty
チェックなし)が破損するためです。
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"
}
}
関数でフィルタリングするだけでなく、含めるキーの配列を受け入れる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>
与えられた
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')
)
どうですか:
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;
}
誰もが言ったように、プロトタイプに手を出さないでください。代わりに、そうするための関数を書くだけです。 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;
};
これらの場合、オブジェクトを処理できる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;
});
私の考え抜いた解決策:
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