जावास्क्रिप्ट: फ़िल्टर () वस्तुओं के लिए


185

ECMAScript 5 में प्रकारों के filter()लिए प्रोटोटाइप है Array, लेकिन Objectप्रकार नहीं हैं, अगर मैं सही ढंग से समझता हूं।

मैं जावास्क्रिप्ट में एस के filter()लिए कैसे लागू करूंगा Object?

मान लें कि मेरे पास यह वस्तु है:

var foo = {
    bar: "Yes"
};

और मैं filter()उस पर काम करना चाहता हूं Object:

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 में जावास्क्रिप्ट त्रुटियाँ मिलती हैं।


विशेष रूप से आपको क्या त्रुटियां मिलती हैं?
NT3RP

आपको क्या त्रुटियां हो रही हैं? यदि संभव हो तो उन्हें पोस्ट करें :)
मानव

वहाँ अस्पष्ट इतिहास wrt jQuery और स्क्रिप्ट है कि विस्तार का एक सा है Object.prototype: Bugs.jquery.com/ticket/2721
वर्धमान ताजा

वास्तव में मुझे क्या चाहिए, सिवाय इसके कि आपको "हटाना होगा!" असली फिल्टर विधि के लिए (इस [कुंजी]) को समर्पित करें।
NoxFly

जवाबों:


200

कभी विस्तार नहीं Object.prototype

आपके कोड में भयानक चीजें होंगी। चीजें टूटेंगी। आप सभी ऑब्जेक्ट प्रकारों का विस्तार कर रहे हैं , जिसमें ऑब्जेक्ट शाब्दिक शामिल हैं।

यहाँ एक त्वरित उदाहरण है जिसे आप आज़मा सकते हैं:

    // 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) {
    let result = {}, key;

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

    return result;
};

61
@ पेप्ट्रिक: एक आदमी को एक रोटी दें और आप उसे एक दिन के लिए खिलाएंगे, उसे कैसे सेंकना सिखाएंगे और आप उसे जीवन भर के लिए खिलाएंगे (या कुछ और, मैं भाई हूं, मुझे सही अंग्रेजी नहीं पता है ;)
मार्टिन जेस्पर्सन

13
आप इसे गलत कर रहे हैं ... (विधेयक (obj [key])) विधेय होना चाहिए (obj [key])
pyrotechnick

6
@pyrotechnick: नहीं। सबसे पहले, उत्तर का मुख्य बिंदु विस्तार नहीं करना है Object.prototype, बल्कि केवल फ़ंक्शन को रखना है Object। दूसरा, यह ओपी का कोड है । स्पष्ट रूप से ओपी का इरादा .filter()ऐसा है कि यह सकारात्मक परिणामों को फ़िल्टर करता है । दूसरे शब्दों में, यह एक नकारात्मक फ़िल्टर है, जहां एक सकारात्मक रिटर्न मान का मतलब है कि इसे परिणाम से बाहर रखा गया है। यदि आप jsFiddle उदाहरण को देखते हैं, तो वह मौजूदा गुणों को छान रहा है undefined
user113716

4
@patrick dw: नहीं। पहले, मैंने विस्तार करने / प्रोटोटाइप का विस्तार नहीं करने का उल्लेख नहीं किया। दूसरा, developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… - "दिए गए फ़ंक्शन द्वारा लागू किए गए परीक्षण को पास करने वाले सभी तत्वों के साथ एक नया सरणी बनाता है।" एक वैश्विक पर सटीक विपरीत लागू करना बहुत मूर्खतापूर्ण लगता है, है ना?
आतिशबाज़ी

8
@pyrotechnick: यह सही है, आपने विस्तारित किए गए प्रोटोटाइप का विस्तार / उल्लेख नहीं किया है, और यह मेरी बात है। आपने कहा कि मैं इसे गलत कर रहा हूं, लेकिन केवल "यह" मैं कर रहा हूं ओपी को विस्तार नहीं करने के लिए कह रहा हूं Object.prototype। इस सवाल से: "यह काम करता है ..., लेकिन जब मैं इसे अपनी साइट पर जोड़ता हूं ..., तो मुझे जावास्क्रिप्ट त्रुटियां मिलती हैं" यदि ओपी .filter()उस के विपरीत व्यवहार के साथ लागू करने का फैसला करता है Array.prototpe.filter, तो यह उसके / उसके ऊपर है। कृपया प्रश्न के तहत एक टिप्पणी छोड़ दें यदि आप ओपी को सूचित करना चाहते हैं कि कोड गलत है, लेकिन मुझे यह मत बताएं कि मैं यह गलत कर रहा हूं जब यह कोड नहीं है।
user113716

279

सबसे पहले, इसका विस्तार करने के लिए बुरा व्यवहार माना जाता हैObject.prototype । इसके बजाय, पर उपयोगिता समारोह के रूप में अपनी सुविधा प्रदान करते हैं Object, वहाँ पहले से ही कर रहे हैं जैसे Object.keys, Object.assign, Object.is, ... आदि।

मैं यहां कई समाधान प्रदान करता हूं:

  1. का उपयोग कर reduceऔरObject.keys
  2. जैसे (1), के साथ संयोजन में Object.assign
  3. के mapबजाय सिंटैक्स का उपयोग करना और फैलानाreduce
  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);

ध्यान दें कि उपरोक्त कोड में predicateएक शामिल करने की शर्त ( इस्तेमाल की गई ओपी के लिए अपवर्जन स्थिति के विपरीत) होनी चाहिए , ताकि यह Array.prototype.filterकाम करने के तरीके के अनुरूप हो ।

2. जैसा (1), के साथ संयोजन में Object.assign

उपरोक्त समाधान में उत्परिवर्तित वस्तु को वापस करने के लिए अल्पविराम ऑपरेटर का उपयोग किया जाता है । यह बेशक एक अभिव्यक्ति के बजाय दो बयानों के रूप में लिखा जा सकता है, लेकिन उत्तरार्द्ध अधिक संक्षिप्त है। अल्पविराम ऑपरेटर के बिना करने के लिए, आप इसके बजाय उपयोग कर सकते हैं , जो उत्परिवर्तित वस्तु को लौटाता है:reduceresObject.assign

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

यहां हम Object.assignकॉल को लूप से बाहर ले जाते हैं , इसलिए इसे केवल एक बार बनाया जाता है, और इसे अलग-अलग तर्कों के रूप में अलग-अलग कुंजी ( प्रसार सिंटैक्स का उपयोग करके ) पास करें:

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( ES2019)।

यह इस "एक-लाइनर" पद्धति की ओर जाता है Object:

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

विधेय फ़ंक्शन को यहाँ तर्क के रूप में एक कुंजी / मान युग्म मिलता है, जो थोड़ा अलग होता है, लेकिन विधेय फ़ंक्शन के तर्क में अधिक संभावनाओं के लिए अनुमति देता है।


क्या यह अधिक जटिल प्रश्न हो सकता है? उदाहरण के लिए:x => x.Expression.Filters.Filter
IamStalker

2
@IamStalker, क्या आपने कोशिश की? इससे कोई फर्क नहीं पड़ता, जब तक आप दूसरे तर्क में एक वैध कार्य प्रदान करते हैं। NB: मुझे नहीं पता कि .Filterअंत में क्या है, लेकिन अगर यह एक समारोह है, तो आपको इसे कॉल करने की आवश्यकता है ( x => x.Expression.Filters.Filter())
trincot

बहुत लंबा! 1 लाइन पर किया जा सकता है। इसे जांचें
अब्देनौर TOUMI

1
नए फीचर्स कम कोड के लिए बना सकते हैं, लेकिन वे धीमे प्रदर्शन के लिए भी बनाते हैं । अधिकांश ब्राउज़रों में एड 3 कंप्लेंट कोड दोगुना से अधिक तेजी से चलता है।
RobG

1
अंतिम संस्करण का टाइपस्क्रिप्ट संस्करण: gist.github.com/OliverJAsh/acafba4f099f6e677db0a38c60dc33d
ओलिवर जोसफ ऐश

20

यदि आप अंडरस्कोर या लॉश का उपयोग करने के लिए तैयार हैं , तो आप pick(या इसके विपरीत omit) का उपयोग कर सकते हैं ।

अंडरस्कोर के डॉक्स से उदाहरण:

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

या कॉलबैक ( लॉश के लिए , पिकबी का उपयोग करें ) के साथ:

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

1
लॉश एक बुरा समाधान है क्योंकि खाली वस्तुओं के लिए फ़िल्टरिंग भी संख्याओं को हटा देगा।
mibbit

मैंने सिर्फ इसके लिए लॉश का इस्तेमाल किया है और यह एक शानदार उपाय है। धन्यवाद @ बोगदान डी!
इरा हरमन

क्या आप अधिक विशिष्ट हो सकते हैं? मेरा मानना ​​है कि कॉलबैक को सही तरीके से लागू करने की बात है
बोगदान डी

10

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

और वापस आ जाएगा :

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

1
अछा लगता है! लेकिन यह सिर्फ दूसरी वस्तु (और "एलिरेज़ा" के नाम / फ़िल्टरवैल्यू के साथ क्यों नहीं लौटता है)?
पिल्ले

1
@Pille, ओपी ने ऐसा करने के लिए कहा ( !predicateअपने स्वयं के कोड के साथ नकारात्मक फ़िल्टर पर ध्यान दें )।
1950

7

जैसा कि पहले ही बताया गया है कि यह एक बुरा विचार है, क्योंकि यह निश्चित रूप से किसी भी 3 पार्टी कोड को तोड़ देगा जिसे आप कभी भी उपयोग करना चाहते हैं।

यदि आप विस्तार करते हैं Object.prototype, तो सभी लाइब्रेरी जैसे कि jquery या प्रोटोटाइप टूट जाएंगे , कारण यह है कि ऑब्जेक्ट्स ( hasOwnPropertyचेक के बिना ) पर आलसी पुनरावृत्ति टूट जाएगी क्योंकि आपके द्वारा जोड़े गए फ़ंक्शन पुनरावृत्ति का हिस्सा होंगे।


स्पष्ट रूप से और स्पष्ट रूप से एक बुरा विचार होने के 'क्यों' की व्याख्या करने के लिए upvoted।
माइकल लिकोरी

5

कैसा रहेगा:

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

4

दिया हुआ

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

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

फिर :

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


3
यह दिए गए विधेय फ़ंक्शन का उपयोग नहीं करता है जैसा कि प्रश्न द्वारा आवश्यक है।
त्रिनकोट

4

मैंने एक Object.filter()ऐसा फंक्शन बनाया है जो न केवल एक फंक्शन द्वारा फिल्टर करता है, बल्कि इसमें कुंजियों की एक सरणी भी शामिल है। वैकल्पिक तीसरा पैरामीटर आपको फ़िल्टर को पलटने की अनुमति देगा।

दिया हुआ:

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

कोड

अस्वीकरण : मैंने संक्षिप्तता के लिए एक्सटी जेएस कोर का उपयोग करने के लिए चुना। क्या ऐसा नहीं लगा कि वस्तु प्रकारों के लिए टाइप चेकर्स लिखना आवश्यक था क्योंकि यह प्रश्न का हिस्सा नहीं था।

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


मेरी वेनिला की जांच करें जवाब और सार
जेड Khullah

1
@trincot धन्यवाद, मैंने इन-प्लेस रिटर्न के बजाय ऑब्जेक्ट की कॉपी वापस करने के लिए प्रतिक्रिया अपडेट की।
श्री पॉलीविरल

2

मेरा राय समाधान:

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/bernardoadc/872d5a174108823159d845cc5baba337


1

यदि आप एक नया बनाने के बजाय उसी ऑब्जेक्ट को म्यूट करना चाहते हैं।

निम्न उदाहरण सभी 0 या रिक्त मान हटा देगा:

const sev = { a: 1, b: 0, c: 3 };
const deleteKeysBy = (obj, predicate) =>
  Object.keys(obj)
    .forEach( (key) => {
      if (predicate(obj[key])) {
        delete(obj[key]);
      }
    });

deleteKeysBy(sev, val => !val);

1

वर्ष 2020 से वेनिला जेएस में समाधान।


let romNumbers={'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}

आप romNumbersकुंजी द्वारा वस्तु को फ़िल्टर कर सकते हैं :

const filteredByKey = Object.fromEntries(Object.entries(romNumbers).filter(([key, value]) => key === 'I'))
// filteredByKey = {I: 1} 

या romNumbersमान द्वारा फ़िल्टर वस्तु:

 const filteredByValue = Object.fromEntries(Object.entries(romNumbers).filter(([key, value]) => value === 5))
 // filteredByValue = {V: 5} 

0

जैसा कि सभी ने कहा, प्रोटोटाइप के साथ चारों ओर पेंच मत करो। इसके बजाय, बस ऐसा करने के लिए एक फ़ंक्शन लिखें। यहाँ मेरा संस्करण है 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

जरूरत पड़ने पर मैं इसका उपयोग करता हूं:

const filterObject = (obj, condition) => {
    const filteredObj = {};
    Object.keys(obj).map(key => {
      if (condition(key)) {
        dataFiltered[key] = obj[key];
      }
    });
  return filteredObj;
}

-1

इन मामलों में मैं jquery $ .map का उपयोग करता हूं, जो वस्तुओं को संभाल सकता है। जैसा कि अन्य उत्तरों में बताया गया है, देशी प्रोटोटाइप ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototyp_chain_Bad_ults_Extension_of_native_prototypesotypes को बदलना अच्छा अभ्यास नहीं है। )

नीचे अपनी वस्तु की कुछ संपत्ति की जाँच करके फ़िल्टर करने का एक उदाहरण है। यदि आपकी स्थिति सही है या undefinedनहीं तो यह स्वयं की वस्तु लौटाता है । undefinedसंपत्ति है कि रिकॉर्ड अपने वस्तु सूची से गायब कर देगा;

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

1
$.mapएक वस्तु ले सकते हैं, लेकिन यह एक सरणी देता है, इसलिए मूल संपत्ति के नाम खो जाते हैं। ओपी को एक फ़िल्टर्ड प्लेन ऑब्जेक्ट की आवश्यकता होती है।
1947
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.