एक वस्तु संदर्भ में जावास्क्रिप्ट स्ट्रिंग को डॉट नोटेशन में बदलें


214

जेएस वस्तु दी

var obj = { a: { b: '1', c: '2' } }

और एक स्ट्रिंग

"a.b"

मैं स्ट्रिंग को डॉट नोटेशन में कैसे बदल सकता हूं ताकि मैं जा सकूं

var val = obj.a.b

यदि स्ट्रिंग बस थी 'a', तो मैं उपयोग कर सकता था obj[a]। लेकिन यह अधिक जटिल है। मुझे लगता है कि कुछ सरल विधि है, लेकिन यह वर्तमान में बच जाता है।


25
@ और्री evalबुराई है; इसे इस्तेमाल न करें
केविनजी

5
FYI करें: यहाँ कुछ दिलचस्प गति परीक्षण हैं जो मैंने अभी किए: jsperf.com/dereference-object-property-path-from-string
जेम्स विल्किंस

अगर perf एक गंभीर विचार है और आप एक ही रास्तों का बहुत अधिक उपयोग कर रहे हैं (जैसे कि एक सरणी फ़िल्टर फ़ंक्शन के अंदर), तो नीचे दिए गए मेरे उत्तर में वर्णित फ़ंक्शन निर्माता का उपयोग करें। जब एक ही मार्ग का हजारों बार उपयोग किया जाता है, तो फंक्शन विधि तेजी से बाहर निकलने या विभाजित होने और हर स्तर पर पथ को कम करने के रूप में 10x से अधिक हो सकती है।
केविन क्रुमले


जवाबों:


437

हालिया नोट: जब मैं चापलूसी कर रहा हूं कि इस उत्तर ने कई उत्थान प्राप्त किए हैं, तो मैं भी कुछ भयभीत हूं। अगर किसी को डॉट-नोटेशन स्ट्रिंग्स को "एक्सएबीसी" जैसे संदर्भों में बदलने की आवश्यकता है, तो यह (शायद) एक संकेत हो सकता है कि कुछ बहुत गलत चल रहा है (जब तक कि शायद आप कुछ अजीब deserialization प्रदर्शन कर रहे हों)।

यह कहना है, इस जवाब के लिए अपना रास्ता खोजने वाले नौसिखियों को खुद से सवाल पूछना चाहिए "मैं ऐसा क्यों कर रहा हूं?"

यह आम तौर पर ऐसा करने के लिए ठीक है अगर आपका उपयोग मामला छोटा है और आप प्रदर्शन के मुद्दों में नहीं चलेंगे, और आपको इसे अधिक जटिल बनाने के लिए अपने अमूर्त पर निर्माण करने की आवश्यकता नहीं होगी। वास्तव में, यदि यह कोड जटिलता को कम करेगा और चीजों को सरल रखेगा, तो आपको संभवतः आगे बढ़ना चाहिए और वही करना चाहिए जो ओपी पूछ रहा है। हालाँकि, अगर ऐसा नहीं है, तो विचार करें कि क्या इनमें से कोई भी लागू होता है:

केस 1 : अपने डेटा के साथ काम करने की प्राथमिक विधि के रूप में (उदाहरण के लिए आपके ऐप का डिफ़ॉल्ट रूप आस-पास से गुजरने वाली वस्तुओं के बारे में और उन्हें dereferencing करना)। जैसे पूछना "मैं एक स्ट्रिंग से एक फ़ंक्शन या चर नाम कैसे देख सकता हूं"।

  • यह खराब प्रोग्रामिंग प्रैक्टिस है (विशेष रूप से अनावश्यक रूप से मेटाप्रोग्रामिंग, और फ़ंक्शन साइड साइड-इफ़ेक्ट-फ्री कोडिंग शैली का उल्लंघन करता है, और इसमें प्रदर्शन हिट होंगे)। इस मामले में खुद को खोजने वाले नोव्स को सरणी अभ्यावेदन के साथ काम करने पर विचार करना चाहिए, उदाहरण के लिए ['x', 'a', 'b', 'c'], या यदि संभव हो तो और भी कुछ प्रत्यक्ष / सरल / सीधा: पहली बार में स्वयं संदर्भों का ट्रैक (सबसे आदर्श यदि यह केवल क्लाइंट-साइड या केवल सर्वर-साइड है), आदि (एक पूर्व-मौजूदा अद्वितीय आईडी जोड़ने के लिए अयोग्य होगा, लेकिन इसका उपयोग तब किया जा सकता है जब युक्ति को अन्यथा इसकी आवश्यकता हो अस्तित्व की परवाह किए बिना।)

स्थिति 2 : क्रमबद्ध डेटा, या डेटा के साथ कार्य करना जो उपयोगकर्ता को प्रदर्शित किया जाएगा। जैसे डेट ऑब्जेक्ट के बजाय एक स्ट्रिंग "1999-12-30" के रूप में एक तारीख का उपयोग करना (जो कि टाइमज़ोन बग पैदा कर सकता है या सावधान नहीं होने पर क्रमबद्ध जटिलता जोड़ सकता है)। या आप जानते हैं कि आप क्या कर रहे हैं।

  • यह शायद ठीक है। सावधान रहें कि कोई डॉट स्ट्रिंग्स नहीं हैं "।" आपके स्वीकृत इनपुट अंशों में।

यदि आप स्वयं को हर समय इस उत्तर का उपयोग करते हुए पाते हैं और स्ट्रिंग और एरे के बीच आगे-पीछे परिवर्तित करते हैं, तो आप खराब स्थिति में हो सकते हैं, और एक विकल्प पर विचार करना चाहिए।

यहाँ एक सुरुचिपूर्ण एक-लाइनर है जो अन्य समाधानों की तुलना में 10x छोटा है:

function index(obj,i) {return obj[i]}
'a.b.etc'.split('.').reduce(index, obj)

[संपादित करें] या ECMAScript 6 में:

'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)

(ऐसा नहीं है कि मुझे लगता है कि eval हमेशा बुरा होता है, जैसा कि अन्य लोग सुझाव देते हैं कि यह (हालांकि यह आमतौर पर है), फिर भी वे लोग प्रसन्न होंगे कि यह विधि eval का उपयोग नहीं करती है। ऊपर obj.a.b.etcदिया गया objऔर स्ट्रिंग मिल जाएगा "a.b.etc"।)

उन लोगों के जवाब में जो अभी भी reduceईसीएमए -262 मानक (5 वें संस्करण) में होने के बावजूद उपयोग करने से डरते हैं , यहां एक दो-पंक्ति पुनरावर्ती कार्यान्वयन है:

function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')

जेएस संकलक कर रहे अनुकूलन के आधार पर, आप यह सुनिश्चित करना चाहते हैं कि किसी भी नेस्टेड फ़ंक्शन को सामान्य तरीकों के माध्यम से हर कॉल पर फिर से परिभाषित नहीं किया जाता है (उन्हें एक क्लोजर, ऑब्जेक्ट या ग्लोबल नेमस्पेस में रखकर)।

संपादित करें :

टिप्पणियों में एक दिलचस्प सवाल का जवाब देने के लिए:

आप इसे एक सेटर में भी कैसे बदलेंगे? न केवल मूल्यों को पथ से लौटाना, बल्कि उन्हें सेट करना यदि कोई नया मान फ़ंक्शन में भेजा जाता है? - स्वेद जून 28 को 21:42 बजे

(sidenote: दुर्भाग्य से एक सेटर के साथ एक वस्तु लौट सकते हैं नहीं, कि के रूप में बुला सम्मेलन का उल्लंघन होगा, टिप्पणीकार के बजाय तरह दुष्प्रभाव के साथ एक सामान्य सेटर शैली समारोह की चर्चा करते हुए किया जा रहा है index(obj,"a.b.etc", value)कर रही है obj.a.b.etc = value।)

reduceशैली वास्तव में उस के लिए उपयुक्त नहीं है, लेकिन हम पुनरावर्ती कार्यान्वयन को संशोधित कर सकते हैं:

function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}

डेमो:

> obj = {a:{b:{etc:5}}}

> index(obj,'a.b.etc')
5
> index(obj,['a','b','etc'])   #works with both strings and lists
5

> index(obj,'a.b.etc', 123)    #setter-mode - third argument (possibly poor form)
123

> index(obj,'a.b.etc')
123

... हालांकि व्यक्तिगत रूप से मैं एक अलग कार्य करने की सलाह दूंगा setIndex(...)। मैं एक साइड-नोट पर समाप्त करना चाहूंगा कि प्रश्न के मूल प्रस्तुतकर्ता को (चाहिए?) सूचकांकों के सरणियों (जो वे प्राप्त कर सकते हैं .split) के साथ काम कर सकते हैं , बजाय तार के; हालांकि आमतौर पर सुविधा समारोह में कुछ भी गलत नहीं है।


एक टिप्पणीकार ने पूछा:

सरणियों के बारे में क्या? "ab [4] .cd [1] [2] [3]" जैसा कुछ? -AlexS

जावास्क्रिप्ट एक बहुत ही अजीब भाषा है; सामान्य वस्तुओं में केवल उनकी संपत्ति की कुंजी के रूप में तार हो सकते हैं, इसलिए उदाहरण के लिए अगर xएक सामान्य वस्तु की तरह था x={}, तो x[1]बन जाएगा x["1"]... आप उस सही को पढ़ते हैं ... हाँ ...

जावास्क्रिप्ट एरर्स (जो स्वयं वस्तु के उदाहरण हैं) विशेष रूप से पूर्णांक कुंजियों को प्रोत्साहित करते हैं, भले ही आप कुछ ऐसा कर सकते थे x=[]; x["puppy"]=5;

लेकिन सामान्य तौर पर (और अपवाद हैं), x["somestring"]===x.somestring(जब इसकी अनुमति हो; आप ऐसा नहीं कर सकते x.123)।

(ध्यान रखें कि जेएस संकलक जो भी आप उपयोग कर रहे हैं, चुन सकते हैं, हो सकता है, इन्हें नीचे सायन अभ्यावेदन के लिए संकलित करें यदि यह साबित कर सकता है कि यह युक्ति का उल्लंघन नहीं करेगा।)

तो आपके प्रश्न का उत्तर इस बात पर निर्भर करेगा कि क्या आप उन वस्तुओं को केवल पूर्णांक (आपके समस्या डोमेन में प्रतिबंध के कारण) स्वीकार करते हैं या नहीं। चलो मान नहीं। फिर एक मान्य अभिव्यक्ति एक आधार पहचानकर्ता का प्लस और कुछ .identifierएस प्लस कुछ ["stringindex"]एस है

यह तब के बराबर होगा a["b"][4]["c"]["d"][1][2][3], हालांकि हमें भी समर्थन करना चाहिए a.b["c\"validjsstringliteral"][3]। आपको एक मान्य स्ट्रिंग शाब्दिक पार्स करने के तरीके को देखने के लिए स्ट्रिंग शाब्दिकों पर पारिस्थितिकीय व्याकरण अनुभाग की जांच करनी होगी । तकनीकी रूप से आप भी जांचना चाहेंगे (मेरे पहले उत्तर के विपरीत) जो aएक मान्य जावास्क्रिप्ट पहचानकर्ता है

अपने प्रश्न का एक साधारण सवाल का जवाब हालांकि, अगर आपके तार अल्पविराम या कोष्ठक शामिल नहीं है , बस सेट में नहीं लंबाई 1+ वर्णों के क्रम मिलाने के लिए किया जाएगा ,या [या ]:

> "abc[4].c.def[1][2][\"gh\"]".match(/[^\]\[.]+/g)
// ^^^ ^  ^ ^^^ ^  ^   ^^^^^
["abc", "4", "c", "def", "1", "2", ""gh""]

यदि आपके तार में भागने के पात्र या "अक्षर नहीं हैं , और क्योंकि IdentifierNames StringLiterals का एक उपवर्ग है (मुझे लगता है कि ???) आप पहले अपने डॉट्स को बदल सकते हैं:

> var R=[], demoString="abc[4].c.def[1][2][\"gh\"]";
> for(var match,matcher=/^([^\.\[]+)|\.([^\.\[]+)|\["([^"]+)"\]|\[(\d+)\]/g; 
      match=matcher.exec(demoString); ) {
  R.push(Array.from(match).slice(1).filter(x=>x!==undefined)[0]);
  // extremely bad code because js regexes are weird, don't use this
}
> R

["abc", "4", "c", "def", "1", "2", "gh"]

बेशक, हमेशा सावधान रहें और कभी भी अपने डेटा पर भरोसा न करें। ऐसा करने के लिए कुछ बुरे तरीके जो कुछ उपयोग मामलों के लिए काम कर सकते हैं उनमें शामिल हैं:

// hackish/wrongish; preprocess your string into "a.b.4.c.d.1.2.3", e.g.: 
> yourstring.replace(/]/g,"").replace(/\[/g,".").split(".")
"a.b.4.c.d.1.2.3"  //use code from before

विशेष 2018 संपादित करें:

चलो पूर्ण-चक्र जाते हैं और सबसे अक्षम, भयावह-ओवरमेटप्रोग्राम किए गए समाधान करते हैं, जो हम साथ आ सकते हैं ... सिंटैक्टिकल शुद्धता हैमिस्टर के हित में । ES6 प्रॉक्सी ऑब्जेक्ट्स के साथ! ... आइए कुछ गुणों को भी परिभाषित करें जो (imho ठीक और अद्भुत हैं लेकिन) अनुचित रूप से लिखे गए पुस्तकालयों को तोड़ सकते हैं। आपको शायद इसका उपयोग करने से सावधान रहना चाहिए यदि आप प्रदर्शन, पवित्रता (आपका या दूसरों का), अपनी नौकरी आदि की परवाह करते हैं।

// [1,2,3][-1]==3 (or just use .slice(-1)[0])
if (![1][-1])
    Object.defineProperty(Array.prototype, -1, {get() {return this[this.length-1]}}); //credit to caub

// WARNING: THIS XTREME™ RADICAL METHOD IS VERY INEFFICIENT,
// ESPECIALLY IF INDEXING INTO MULTIPLE OBJECTS,
// because you are constantly creating wrapper objects on-the-fly and,
// even worse, going through Proxy i.e. runtime ~reflection, which prevents
// compiler optimization

// Proxy handler to override obj[*]/obj.* and obj[*]=...
var hyperIndexProxyHandler = {
    get: function(obj,key, proxy) {
        return key.split('.').reduce((o,i)=>o[i], obj);
    },
    set: function(obj,key,value, proxy) {
        var keys = key.split('.');
        var beforeLast = keys.slice(0,-1).reduce((o,i)=>o[i], obj);
        beforeLast[keys[-1]] = value;
    },
    has: function(obj,key) {
        //etc
    }
};
function hyperIndexOf(target) {
    return new Proxy(target, hyperIndexProxyHandler);
}

डेमो:

var obj = {a:{b:{c:1, d:2}}};
console.log("obj is:", JSON.stringify(obj));

var objHyper = hyperIndexOf(obj);
console.log("(proxy override get) objHyper['a.b.c'] is:", objHyper['a.b.c']);
objHyper['a.b.c'] = 3;
console.log("(proxy override set) objHyper['a.b.c']=3, now obj is:", JSON.stringify(obj));

console.log("(behind the scenes) objHyper is:", objHyper);

if (!({}).H)
    Object.defineProperties(Object.prototype, {
        H: {
            get: function() {
                return hyperIndexOf(this); // TODO:cache as a non-enumerable property for efficiency?
            }
        }
    });

console.log("(shortcut) obj.H['a.b.c']=4");
obj.H['a.b.c'] = 4;
console.log("(shortcut) obj.H['a.b.c'] is obj['a']['b']['c'] is", obj.H['a.b.c']);

आउटपुट:

obj है: {"a": {"b": {"c": 1, "d": 2}}}

(प्रॉक्सी ओवरराइड मिलता है) objHyper ['abc'] है: 1

(प्रॉक्सी ओवरराइड सेट) objHyper ['abc'] = 3, अब obj है: {"a": {"b": {"c": 3, "d": 2}}}

(पर्दे के पीछे) objHyper है: प्रॉक्सी {a: {…}}

(शॉर्टकट) obj.H ['abc'] = 4

(शॉर्टकट) obj.H ['abc'] obj ['a'] ['b'] ['c' है: 4

अक्षम विचार: आप इनपुट तर्क के आधार पर प्रेषण के लिए ऊपर संशोधित कर सकते हैं; या तो .match(/[^\]\[.]+/g)समर्थन करने के लिए विधि का उपयोग करें obj['keys'].like[3]['this'], या यदि instanceof Array, तो इनपुट के रूप में एक ऐरे को स्वीकार करें keys = ['a','b','c']; obj.H[keys]


प्रति सुझाव है कि शायद आप 'नरम' NaN- शैली के तरीके में अपरिभाषित सूचकांकों को संभालना चाहते हैं (जैसे index({a:{b:{c:...}}}, 'a.x.c')बिना पढ़े टाइपर्रर के बजाय अनिर्धारित वापसी) ...:

1) यह 1-आयामी सूचकांक स्थिति ({}) ['जैसे'] == अपरिभाषित में "हमें एक त्रुटि फेंकने के बजाय अपरिभाषित लौटना चाहिए," के परिप्रेक्ष्य से समझ में आता है, इसलिए "हमें फेंकने के बजाय अपरिभाषित लौटना चाहिए" त्रुटि "एन-आयामी स्थिति में।

2) इसका मतलब यह नहीं है कि हम जो परिप्रेक्ष्य कर रहे हैं x['a']['x']['c'], उससे ऊपर के उदाहरण में टाइपर्रर के साथ असफल हो जाएंगे।

उसने कहा, आप अपने कम करने के कार्य को बदलकर यह काम करेंगे:

(o,i)=>o===undefined?undefined:o[i], या (o,i)=>(o||{})[i]

(जब आप अगली बार अनुक्रमणिका को अपरिभाषित कर रहे हों, या जब आप ऐसी असफलताओं की पर्याप्त रूप से दुर्लभ होने की उम्मीद करते हैं, तो प्रयोग करने पर आप लूप और ब्रेकिंग / रिटर्न के लिए उपयोग करके इसे और अधिक कुशल बना सकते हैं।)


4
reduceवर्तमान में उपयोग किए गए सभी ब्राउज़रों में समर्थित नहीं है।
रिकार्डो तोमासी

14
@ रिकार्डो: Array.reduceECMA-262 मानक का हिस्सा है। यदि आप वास्तव में पुराने ब्राउज़रों का समर्थन करना चाहते हैं, तो आप Array.prototype.reduceकहीं दिए गए नमूना कार्यान्वयन को परिभाषित कर सकते हैं (जैसे developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… )।
निंजाजेको

2
हां, लेकिन दोनों लाइनों को एक फंक्शन में रखना काफी आसान है। var setget = function( obj, path ){ function index( robj,i ) {return robj[i]}; return path.split('.').reduce( index, obj ); }
nevf

3
मुझे यह सुरुचिपूर्ण उदाहरण बहुत पसंद है, धन्यवाद निन्जाकोको। मैंने इसे सरणी शैली संकेतन, साथ ही खाली तारों को संभालने के लिए बढ़ाया है - मेरा उदाहरण यहां देखें: jsfiddle.net/sc0ttyd/q7zyd
Sc0ttyD

2
@njjagecko आप इसे एक सेटर में भी कैसे बदलेंगे? न केवल पथ द्वारा मान लौटाते हैं, बल्कि फ़ंक्शन में एक नया मान भेजे जाने पर उन्हें सेट करते हैं?
स्वेद

64

यदि आप लिटाश का उपयोग कर सकते हैं , तो एक फ़ंक्शन है, जो वास्तव में ऐसा करता है:

_.get (ऑब्जेक्ट, पथ, [defaultValue])

var val = _.get(obj, "a.b");

4
नोट: _.get(object, path)अगर कोई रास्ता नहीं मिला तो नहीं टूटेगा। 'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)कर देता है। मेरे विशिष्ट मामले के लिए - हर मामले के लिए नहीं - ठीक वैसा ही जैसा मुझे चाहिए था। धन्यवाद!
श्री बी।

1
@ Mr.B। Lodash के नवीनतम संस्करण में एक तीसरा, वैकल्पिक, तर्क है defaultValue_.get()विधि रिटर्न यदि डिफ़ॉल्ट मान _.get()विश्लेषण करता undefinedहै, इसलिए यह निर्धारित कर सकते हैं करने के लिए आप चाहते हैं और मूल्य आप सेट के लिए घड़ी।
भाप से चलने वाली

7
किसी को भी आश्चर्य हो, यह भी समर्थन करता है _.set(object, path, value)
जेफरी रोसडेन्गल

19

पुनरावृत्ति के साथ थोड़ा और शामिल उदाहरण।

function recompose(obj,string){
    var parts = string.split('.');
    var newObj = obj[parts[0]];
    if(parts[1]){
        parts.splice(0,1);
        var newString = parts.join('.');
        return recompose(newObj,newString);
    }
    return newObj;
}


var obj = { a: { b: '1', c: '2', d:{a:{b:'blah'}}}};

alert(recompose(obj,'a.d.a.b')); //blah

19

तुम भी lodash.get का उपयोग कर सकते हैं

आप बस इस पैकेज को स्थापित करें (npm i --save lodash.get) और फिर इसे इस तरह उपयोग करें:

const get = require('lodash.get');

const myObj = { user: { firstName: 'Stacky', lastName: 'Overflowy' }, id: 123 };

console.log(get(myObj, 'user.firstName')); // prints Stacky
console.log(get(myObj, 'id')); //prints  123

//You can also update values
get(myObj, 'user').firstName = John;

10

यदि आप एक ही पथ को कई बार बाधित करने की अपेक्षा करते हैं, तो प्रत्येक डॉट संकेतन पथ के लिए एक फ़ंक्शन का निर्माण वास्तव में अब तक का सबसे अच्छा प्रदर्शन है (ऊपर दिए गए टिप्पणियों में जुड़े जेम्स विल्किंस पर परिपूर्ण परीक्षण)।

var path = 'a.b.x';
var getter = new Function("obj", "return obj." + path + ";");
getter(obj);

फ़ंक्शन कंस्ट्रक्टर का उपयोग सुरक्षा और सबसे खराब स्थिति के प्रदर्शन के रूप में eval () के रूप में कुछ ही कमियां हैं, लेकिन IMO यह उन मामलों के लिए एक बुरी तरह से अप्रयुक्त उपकरण है जहां आपको चरम गतिशीलता और उच्च प्रदर्शन के संयोजन की आवश्यकता होती है। मैं सरणी फ़िल्टर फ़ंक्शंस बनाने और उन्हें AngularJS डाइजेस्ट लूप के अंदर कॉल करने के लिए इस पद्धति का उपयोग करता हूं। मेरे प्रोफाइल लगातार array.filter () चरण को कम से कम 1ms से घटाते हुए दिखाते हैं और 2000 जटिल वस्तुओं के बारे में छानते हैं, गतिशील रूप से परिभाषित रास्तों का उपयोग करते हुए 3-4 स्तर गहरा है।

सेटर फ़ंक्शंस बनाने के लिए एक समान कार्यप्रणाली का इस्तेमाल किया जा सकता है:

var setter = new Function("obj", "newval", "obj." + path + " = newval;");
setter(obj, "some new val");

1
अगर आपको समान रास्तों को लंबे समय तक अलग करने की आवश्यकता है, तो ऊपर दिए गए jsperf.com लिंक इस बात का एक उदाहरण दिखाता है कि बाद में फ़ंक्शन को कैसे सहेजना और देखना है। फंक्शन कंस्ट्रक्टर को कॉल करने की क्रिया काफी धीमी है, इसलिए उच्च-स्तरीय कोड को यदि संभव हो तो इसे दोहराने से बचने के लिए परिणामों को याद रखना चाहिए।
केविन क्रुमले

9

मूल पद से कई वर्ष। अब there वस्तु-पथ ’नामक एक महान पुस्तकालय है। https://github.com/mariocasciaro/object-path

एनपीएम और बेस पर उपलब्ध https://www.npmjs.com/package/object-path

यह उतना आसान है:

objectPath.get(obj, "a.c.1");  //returns "f"
objectPath.set(obj, "a.j.0.f", "m");

और गहरी नेस्टेड गुणों और सरणियों के लिए काम करता है।


7

मैं पथ को विभाजित करने और इसे पुनरावृत्त करने और आपके पास मौजूद वस्तु को कम करने का सुझाव देता हूं। यह प्रस्ताव लापता गुणों के लिए एक डिफ़ॉल्ट मान के साथ काम करता है ।

const getValue = (object, keys) => keys.split('.').reduce((o, k) => (o || {})[k], object);

console.log(getValue({ a: { b: '1', c: '2' } }, 'a.b'));
console.log(getValue({ a: { b: '1', c: '2' } }, 'foo.bar.baz'));


6

ध्यान दें कि यदि आप पहले से ही लोडश का उपयोग कर रहे हैं तो आप propertyया getकार्यों का उपयोग कर सकते हैं :

var obj = { a: { b: '1', c: '2' } };
_.property('a.b')(obj); // => 1
_.get(obj, 'a.b'); // => 1

अंडरस्कोर का भी एक propertyकार्य है, लेकिन यह डॉट नोटेशन का समर्थन नहीं करता है।


1
लगता है अंडरस्कोर डॉट नोटेशन का समर्थन नहीं करता है। उत्तर अपडेट किया गया।
तामलिन 22:16

5

अन्य प्रस्ताव थोड़े गूढ़ हैं, इसलिए मुझे लगा कि मैं इसमें योगदान दूंगा:

Object.prop = function(obj, prop, val){
    var props = prop.split('.')
      , final = props.pop(), p 
    while(p = props.shift()){
        if (typeof obj[p] === 'undefined')
            return undefined;
        obj = obj[p]
    }
    return val ? (obj[final] = val) : obj[final]
}

var obj = { a: { b: '1', c: '2' } }

// get
console.log(Object.prop(obj, 'a.c')) // -> 2
// set
Object.prop(obj, 'a.c', function(){})
console.log(obj) // -> { a: { b: '1', c: [Function] } }

5

आप npm पर उपलब्ध पुस्तकालय का उपयोग कर सकते हैं, जो इस प्रक्रिया को सरल करता है। https://www.npmjs.com/package/dot-object

 var dot = require('dot-object');

var obj = {
 some: {
   nested: {
     value: 'Hi there!'
   }
 }
};

var val = dot.pick('some.nested.value', obj);
console.log(val);

// Result: Hi there!

1
इसे हमारे ध्यान में लाने के लिए धन्यवाद। बहुत उपयोगी लगता है।
nevf

और यही कारण है कि प्रत्येक परियोजना 10000 निर्भरताएं इंजेक्ट करती है :-)।
मार्विन

4
var a = { b: { c: 9 } };

function value(layer, path, value) {
    var i = 0,
        path = path.split('.');

    for (; i < path.length; i++)
        if (value != null && i + 1 === path.length)
            layer[path[i]] = value;
        layer = layer[path[i]];

    return layer;
};

value(a, 'b.c'); // 9

value(a, 'b.c', 4);

value(a, 'b.c'); // 4

जब यह करने के बहुत सरल evalतरीके से तुलना की जाती है तो यह बहुत कोड होता है, लेकिन जैसा कि साइमन विलिसन कहते हैं, आपको कभी भी उपयोग नहीं करना चाहिए

इसके अलावा, JSFiddle


महान, यह एक इलाज का काम करता है और सीडी सांचेज़ से छोटा है। धन्यवाद।
नेव्स

मान (,, 'bbbbb') अपरिभाषित नहीं लौटता है
frumbert

4

मैंने निन्जाजेको द्वारा सुरुचिपूर्ण उत्तर दिया है ताकि यह फ़ंक्शन बिंदीदार और / या सरणी शैली दोनों संदर्भों को संभालता है, और इसलिए कि खाली स्ट्रिंग के कारण मूल ऑब्जेक्ट वापस आ जाता है।

हेयर यू गो:

string_to_ref = function (object, reference) {
    function arr_deref(o, ref, i) { return !ref ? o : (o[ref.slice(0, i ? -1 : ref.length)]) }
    function dot_deref(o, ref) { return ref.split('[').reduce(arr_deref, o); }
    return !reference ? object : reference.split('.').reduce(dot_deref, object);
};

मेरे काम करने वाले jsFiddle उदाहरण यहां देखें: http://jsfiddle.net/sc0ttyd/q7zyd/


वास्तव में अच्छा समाधान है। केवल एक समस्या है, यह मानता []है कि सरणियों के लिए अंकन हमेशा होता है। वहाँ हो सकता है कि इस तरह के रूप में अच्छी तरह से उदाहरण के obj['some-problem/name'].list[1]लिए वस्तु कुंजी का प्रतिनिधित्व किया है इसे ठीक करने के लिए मुझे arr_derefइस तरह से फ़ंक्शन को अपडेट करना पड़ाjavascript function arr_deref(o, ref, i) { return !ref ? o : (o[(ref.slice(0, i ? -1 : ref.length)).replace(/^['"]|['"]$/g, '')]); }
सेर्केन यर्सन

1
हालाँकि, आजकल, मैं ऐसा नहीं करता। मैं Lodash: lodash.com/docs#get
Sc0ttyD

2
var find = function(root, path) {
  var segments = path.split('.'),
      cursor = root,
      target;

  for (var i = 0; i < segments.length; ++i) {
   target = cursor[segments[i]];
   if (typeof target == "undefined") return void 0;
   cursor = target;
  }

  return cursor;
};

var obj = { a: { b: '1', c: '2' } }
find(obj, "a.b"); // 1

var set = function (root, path, value) {
   var segments = path.split('.'),
       cursor = root,
       target;

   for (var i = 0; i < segments.length - 1; ++i) {
      cursor = cursor[segments[i]] || { };
   }

   cursor[segments[segments.length - 1]] = value;
};

set(obj, "a.k", function () { console.log("hello world"); });

find(obj, "a.k")(); // hello world

सभी त्वरित प्रतिक्रियाओं के लिए धन्यवाद। Eval () समाधान पसंद नहीं है। यह और इसी तरह की पोस्ट सबसे अच्छी लगती हैं। हालाँकि मुझे अभी भी समस्या हो रही है। मैं मान obj.ab = नया मान सेट करने का प्रयास कर रहा हूं। सटीक बी का मान होना एक फ़ंक्शन है इसलिए मुझे obj.ab (new_value) का उपयोग करने की आवश्यकता है। फ़ंक्शन को कहा जाता है, लेकिन मान सेट नहीं है। मुझे लगता है कि यह एक गुंजाइश मुद्दा है लेकिन मैं अभी भी खुदाई कर रहा हूं। मुझे लगता है कि यह मूल प्रश्न के दायरे से बाहर है। मेरा कोड Knockout.js का उपयोग कर रहा है और b एक ko.observable है।
nevf

@nevf: मैंने एक दूसरा फंक्शन जोड़ा है जो मुझे लगता है कि आपको वही चाहिए जो आप चाहते हैं। आप अपनी पसंद के व्यवहार के आधार पर इसे अपनी पसंद के अनुसार कस्टमाइज़ कर सकते हैं (जैसे कि यदि वे मौजूद नहीं हैं तो वस्तुओं को बनाना चाहिए ?, आदि)।
क्रिस्टियन सांचेज

@nevf लेकिन मेरा यह एक फ़ंक्शन के साथ है। ; डी
मैककेला

अपडेट के लिए धन्यवाद जो मैं उपयोग करने में सक्षम था। @tylermwashburn - और आपके छोटे कार्यान्वयन के लिए धन्यवाद जो एक उपचार का काम करता है। एक महान डब्ल्यू / ई सब है।
नेवफ

2

आप कोड की एक पंक्ति के साथ डॉट नोटेशन द्वारा किसी वस्तु सदस्य का मूल्य प्राप्त कर सकते हैं:

new Function('_', 'return _.' + path)(obj);

आप मामले में:

var obj = { a: { b: '1', c: '2' } }
var val = new Function('_', 'return _.a.b')(obj);

इसे सरल बनाने के लिए आप इस तरह से एक फंक्शन लिख सकते हैं:

function objGet(obj, path){
    return new Function('_', 'return _.' + path)(obj);
}

स्पष्टीकरण:

फ़ंक्शन निर्माता एक नया फ़ंक्शन ऑब्जेक्ट बनाता है। जावास्क्रिप्ट में हर फ़ंक्शन वास्तव में एक फंक्शन ऑब्जेक्ट है। फ़ंक्शन निर्माता के साथ स्पष्ट रूप से फ़ंक्शन बनाने का सिंटैक्स है:

new Function ([arg1[, arg2[, ...argN]],] functionBody)

जहाँ arguments(arg1 to argN)एक स्ट्रिंग होना चाहिए जो एक मान्य javaScript आइडेंटिफ़ायर से मेल खाती है और functionBodyएक स्ट्रिंग है जिसमें javaScript स्टेटमेंट हैं जिसमें फ़ंक्शन परिभाषा शामिल है।

हमारे मामले में हम डॉट संकेतन के साथ ऑब्जेक्ट सदस्य को पुनः प्राप्त करने के लिए स्ट्रिंग फ़ंक्शन बॉडी का लाभ उठाते हैं।

आशा करता हूँ की ये काम करेगा।


क्या आप ठीक से समझा सकते हैं कि यह क्या कर रहा है? और आप फंक्शन पैरामीटर के रूप में 'ab' आदि कैसे पास करेंगे?
नीव

1
मैंने इसे एक +1 दिया लेकिन JSLint ने चेतावनी दी है कि "फंक्शन कंस्ट्रक्टर एक प्रकार का निष्कासन है"
gabe

2

GET / SET उत्तर जो प्रतिक्रिया मूलक में भी काम करता है (आप Object.prototypeवर्तमान में असाइन नहीं कर सकते हैं ):

Object.defineProperty(Object.prototype, 'getNestedProp', {
    value: function(desc) {
        var obj = this;
        var arr = desc.split(".");
        while(arr.length && (obj = obj[arr.shift()]));
        return obj;
    },
    enumerable: false
});

Object.defineProperty(Object.prototype, 'setNestedProp', {
    value: function(desc, value) {
        var obj = this;
        var arr = desc.split(".");
        var last = arr.pop();
        while(arr.length && (obj = obj[arr.shift()]));
        obj[last] = value;
    },
    enumerable: false
});

उपयोग:

var a = { values: [{ value: null }] };
var b = { one: { two: 'foo' } };

a.setNestedProp('values.0.value', b.getNestedProp('one.two'));
console.log(a.values[0].value); // foo

1

मैंने रिकार्डो टोमासी के उत्तर से निम्नलिखित को कॉपी किया और उप-ऑब्जेक्ट बनाने के लिए संशोधित किया जो अभी तक आवश्यक नहीं है। यह थोड़ा कम कुशल (अधिक ifएस और खाली वस्तुओं का निर्माण) है, लेकिन बहुत अच्छा होना चाहिए।

इसके अलावा, यह हमें वह करने की अनुमति देगा Object.prop(obj, 'a.b', false)जहाँ हम पहले नहीं कर सकते थे। दुर्भाग्य से, यह अभी भी हमें काम नहीं करने देगा undefined... निश्चित नहीं कि उस बारे में अभी तक कैसे जाना है।

/**
 * Object.prop()
 *
 * Allows dot-notation access to object properties for both getting and setting.
 *
 * @param {Object} obj    The object we're getting from or setting
 * @param {string} prop   The dot-notated string defining the property location
 * @param {mixed}  val    For setting only; the value to set
 */
 Object.prop = function(obj, prop, val){
   var props = prop.split('.'),
       final = props.pop(),
       p;

   for (var i = 0; i < props.length; i++) {
     p = props[i];
     if (typeof obj[p] === 'undefined') {
       // If we're setting
       if (typeof val !== 'undefined') {
         // If we're not at the end of the props, keep adding new empty objects
         if (i != props.length)
           obj[p] = {};
       }
       else
         return undefined;
     }
     obj = obj[p]
   }
   return typeof val !== "undefined" ? (obj[final] = val) : obj[final]
 }

1

यदि आप किसी भी ऑब्जेक्ट को बदलना चाहते हैं जिसमें डॉट नोटेशन कुंजी उन कुंजियों के एक सरणी संस्करण में है जो आप इसका उपयोग कर सकते हैं।


यह कुछ इस तरह परिवर्तित होगा

{
  name: 'Andy',
  brothers.0: 'Bob'
  brothers.1: 'Steve'
  brothers.2: 'Jack'
  sisters.0: 'Sally'
}

सेवा

{
  name: 'Andy',
  brothers: ['Bob', 'Steve', 'Jack']
  sisters: ['Sally']
}

convertDotNotationToArray(objectWithDotNotation) {

    Object.entries(objectWithDotNotation).forEach(([key, val]) => {

      // Is the key of dot notation 
      if (key.includes('.')) {
        const [name, index] = key.split('.');

        // If you have not created an array version, create one 
        if (!objectWithDotNotation[name]) {
          objectWithDotNotation[name] = new Array();
        }

        // Save the value in the newly created array at the specific index 
        objectWithDotNotation[name][index] = val;
        // Delete the current dot notation key val
        delete objectWithDotNotation[key];
      }
    });

}

यह JSON की तरह "Brothers.0.one" के साथ मूल्यों को संसाधित नहीं करता है।
कार्तिक

क्या आपके पास सरणी संग्रह के साथ अधिक जटिल JSON के प्रसंस्करण के लिए कोई सुझाव है।?
कार्तिक

0

यहाँ मेरा कोड बिना उपयोग के है eval। इसे समझना भी आसान है।

function value(obj, props) {
  if (!props) return obj;
  var propsArr = props.split('.');
  var prop = propsArr.splice(0, 1);
  return value(obj[prop], propsArr.join('.'));
}

var obj = { a: { b: '1', c: '2', d:{a:{b:'blah'}}}};

console.log(value(obj, 'a.d.a.b')); //returns blah

0

हां, यह 4 साल पहले पूछा गया था और हां, बेस प्रोटोटाइप का विस्तार आमतौर पर अच्छा विचार नहीं है लेकिन, यदि आप सभी एक्सटेंशन को एक स्थान पर रखते हैं, तो वे उपयोगी हो सकते हैं।
तो, यहाँ ऐसा करने का मेरा तरीका है।

   Object.defineProperty(Object.prototype, "getNestedProperty", {
    value     : function (propertyName) {
        var result = this;
        var arr = propertyName.split(".");

        while (arr.length && result) {
            result = result[arr.shift()];
        }

        return result;
    },
    enumerable: false
});

अब आप फ़ंक्शन या कॉपी / पेस्टिंग फ़ंक्शन के साथ मॉड्यूल आयात किए बिना हर जगह नेस्टेड संपत्ति प्राप्त करने में सक्षम होंगे।

UPD.Example:

{a:{b:11}}.getNestedProperty('a.b'); //returns 11

UPD 2. अगला विस्तार मेरी परियोजना में आम को तोड़ता है। इसके अलावा मैंने पढ़ा है कि यह jquery को तोड़ सकता है। इसलिए, इसे अगले तरीके से कभी न करें

 Object.prototype.getNestedProperty = function (propertyName) {
    var result = this;
    var arr = propertyName.split(".");

    while (arr.length && result) {
        result = result[arr.shift()];
    }

    return result;
};

0

यहाँ मेरा कार्यान्वयन है

कार्यान्वयन १

Object.prototype.access = function() {
    var ele = this[arguments[0]];
    if(arguments.length === 1) return ele;
    return ele.access.apply(ele, [].slice.call(arguments, 1));
}

कार्यान्वयन 2 (स्लाइस के बजाय ऐरे को कम करके)

Object.prototype.access = function() {
    var self = this;
    return [].reduce.call(arguments,function(prev,cur) {
        return prev[cur];
    }, self);
}

उदाहरण:

var myobj = {'a':{'b':{'c':{'d':'abcd','e':[11,22,33]}}}};

myobj.access('a','b','c'); // returns: {'d':'abcd', e:[0,1,2,3]}
myobj.a.b.access('c','d'); // returns: 'abcd'
myobj.access('a','b','c','e',0); // returns: 11

यह भी सरणियों के अंदर वस्तुओं को संभाल सकता है

var myobj2 = {'a': {'b':[{'c':'ab0c'},{'d':'ab1d'}]}}
myobj2.access('a','b','1','d'); // returns: 'ab1d'

0

यह मेरे द्वारा प्रस्तावित समाधान है: Ninjagecko

मेरे लिए साधारण स्ट्रिंग संकेतन पर्याप्त नहीं था, इसलिए नीचे दिए गए संस्करण निम्न बातों का समर्थन करते हैं:

index (obj, 'data.accounts [0] .address [0] .postcode');

/**
 * Get object by index
 * @supported
 * - arrays supported
 * - array indexes supported
 * @not-supported
 * - multiple arrays
 * @issues:
 *  index(myAccount, 'accounts[0].address[0].id') - works fine
 *  index(myAccount, 'accounts[].address[0].id') - doesnt work
 * @Example:
 * index(obj, 'data.accounts[].id') => returns array of id's
 * index(obj, 'data.accounts[0].id') => returns id of 0 element from array
 * index(obj, 'data.accounts[0].addresses.list[0].id') => error
 * @param obj
 * @param path
 * @returns {any}
 */
var index = function(obj, path, isArray?, arrIndex?){

    // is an array
    if(typeof isArray === 'undefined') isArray = false;
    // array index,
    // if null, will take all indexes
    if(typeof arrIndex === 'undefined') arrIndex = null;

    var _arrIndex = null;

    var reduceArrayTag = function(i, subArrIndex){
        return i.replace(/(\[)([\d]{0,})(\])/, (i) => {
            var tmp = i.match(/(\[)([\d]{0,})(\])/);
            isArray = true;
            if(subArrIndex){
                _arrIndex =  (tmp[2] !== '') ? tmp[2] : null;
            }else{
                arrIndex =  (tmp[2] !== '') ? tmp[2] : null;
            }
            return '';
        });
    }

    function byIndex(obj, i) {
        // if is an array
        if(isArray){
            isArray = false;
            i = reduceArrayTag(i, true);
            // if array index is null,
            // return an array of with values from every index
            if(!arrIndex){
                var arrValues = [];
                _.forEach(obj, (el) => {
                    arrValues.push(index(el, i, isArray, arrIndex));
                })
                return arrValues;
            }
            // if array index is specified
            var value = obj[arrIndex][i];
            if(isArray){
                arrIndex = _arrIndex;
            }else{
                arrIndex = null;
            }
            return value;
        }else{
            // remove [] from notation,
            // if [] has been removed, check the index of array
            i = reduceArrayTag(i, false);
            return obj[i]
        }
    }

    // reduce with byIndex method
    return path.split('.').reduce(byIndex, obj)
}

0

एक मरे हुए घोड़े की पिटाई के जोखिम पर ... मुझे यह पता लगाने के लिए निहित वस्तुओं का पता लगाने में सबसे उपयोगी है कि आप आधार वस्तु के संबंध में या समान संरचना वाले समान ऑब्जेक्ट पर हैं। उस अंत तक, यह एक नेस्टेड ऑब्जेक्ट ट्रैवर्सल फ़ंक्शन के साथ उपयोगी है। ध्यान दें कि मैंने पथ को पकड़ने के लिए एक सरणी का उपयोग किया है। यह एक स्ट्रिंग पथ या एक सरणी का उपयोग करने के लिए इसे संशोधित करने के लिए तुच्छ होगा। यह भी ध्यान दें कि आप "अपरिभाषित" को कुछ अन्य कार्यान्वयनों के विपरीत, मूल्य पर असाइन कर सकते हैं।

/*
 * Traverse each key in a nested object and call fn(curObject, key, value, baseObject, path)
 * on each. The path is an array of the keys required to get to curObject from
 * baseObject using objectPath(). If the call to fn() returns falsey, objects below
 * curObject are not traversed. Should be called as objectTaverse(baseObject, fn).
 * The third and fourth arguments are only used by recursion.
 */
function objectTraverse (o, fn, base, path) {
    path = path || [];
    base = base || o;
    Object.keys(o).forEach(function (key) {
        if (fn(o, key, o[key], base, path) && jQuery.isPlainObject(o[key])) {
            path.push(key);
            objectTraverse(o[key], fn, base, path);
            path.pop();
        }
    });
}

/*
 * Get/set a nested key in an object. Path is an array of the keys to reference each level
 * of nesting. If value is provided, the nested key is set.
 * The value of the nested key is returned.
 */
function objectPath (o, path, value) {
    var last = path.pop();

    while (path.length && o) {
        o = o[path.shift()];
    }
    if (arguments.length < 3) {
        return (o? o[last] : o);
    }
    return (o[last] = value);
}


0

मैंने अपने प्रोजेक्ट में इस कोड का उपयोग किया है

const getValue = (obj, arrPath) => (
  arrPath.reduce((x, y) => {
    if (y in x) return x[y]
    return {}
  }, obj)
)

उपयोग:

const obj = { id: { user: { local: 104 } } }
const path = [ 'id', 'user', 'local' ]
getValue(obj, path) // return 104

0

कुछ साल बाद, मैंने पाया कि यह गुंजाइश और सरणी को संभालता है। जैसेa['b']["c"].d.etc

function getScopedObj(scope, str) {
  let obj=scope, arr;

  try {
    arr = str.split(/[\[\]\.]/) // split by [,],.
      .filter(el => el)             // filter out empty one
      .map(el => el.replace(/^['"]+|['"]+$/g, '')); // remove string quotation
    arr.forEach(el => obj = obj[el])
  } catch(e) {
    obj = undefined;
  }

  return obj;
}

window.a = {b: {c: {d: {etc: 'success'}}}}

getScopedObj(window, `a.b.c.d.etc`)             // success
getScopedObj(window, `a['b']["c"].d.etc`)       // success
getScopedObj(window, `a['INVALID']["c"].d.etc`) // undefined

-1

यह स्पष्ट नहीं है कि आपका प्रश्न क्या है। अपनी वस्तु को देखते हुए, obj.a.bआपको "2" वैसा ही देगा जैसा वह है। यदि आप ब्रैकेट का उपयोग करने के लिए स्ट्रिंग में हेरफेर करना चाहते हैं, तो आप ऐसा कर सकते हैं:

var s = 'a.b';
s = 'obj["' + s.replace(/\./g, '"]["') + '"]';
alert(s); // displays obj["a"]["b"]

1
यह काम नहीं करता है a.b.c, और वास्तव में वे जो चाहते हैं उसे पूरा नहीं करते हैं। वे मूल्य चाहते हैं, evalपथ नहीं ।
मैकायला

मैंने अब इसे ठीक कर दिया इसलिए यह साथ काम करता है a.b.c, लेकिन आप सही हैं, जाहिर है वह संपत्ति का मूल्य प्राप्त / निर्धारित करना चाहता था obj.a.b। सवाल मेरे लिए उलझन भरा था, क्योंकि उन्होंने कहा कि वह "स्ट्रिंग बदलना" चाहते थे ....
मार्क इरिच

बहुत बढ़िया। :) यह थोड़ा अस्पष्ट था। आपने हालांकि रूपांतरण का अच्छा काम किया।
मैकायला

-1

यहाँ मेरे 10 सेंट केस करने के लिए, bellow फ़ंक्शन दिए गए पथ के आधार पर मिलेगा / सेट होगा, .. यकीन है कि u इसे सुधार सकते हैं, हटा सकते हैं || और Object.hasOwnPropertyयदि आप गलत तरीके से गलत मूल्यों के बारे में परवाह करते हैं, तो इसे बदल दें ,

मैं इसे a.b.cऔर ab2.c के साथ परीक्षण किया {a:{b:[0,1,{c:7}]}}और इसकी सेटिंग और प्राप्त करने दोनों के लिए काम करता है :)।

Cheerz

function helper(obj, path, setValue){
  const l = String(path).split('.');
  return l.reduce((o,i, idx)=>{
   if( l.length-idx===1)  { o[i] = setValue || o[i];return setValue ? obj : o[i];}
  o[i] = o[i] || {};
   return o[i];
  }, x)
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.