JavaScript में arrays के साथ काम करने का मतलब अब शायद ही कभी यह होता है कि आपको for loop लिखने की ज़रूरत पड़े। भाषा अब आपको ऐसे अभिव्यंजक (expressive) टूल्स देती है जो जटिल और शोर भरे डेटा को साफ और पठनीय (readable) रूपांतरणों में बदल देते हैं। यह गाइड वहीं से शुरू होती है जहाँ बुनियादी बातें खत्म होती हैं—इसमें flattening, reducing, querying और नए immutable patterns पर ध्यान केंद्रित किया गया है जो आपके मूल डेटा को अप्रभावित रखते हैं।
flat() और flatMap() के साथ नेस्टेड स्ट्रक्चर्स को फ्लैट करना (Flattening)
वास्तविक दुनिया का डेटा अक्सर नेस्टेड होता है। किसी API से रिकॉर्ड प्राप्त करना, CSV को पार्स करना, या कॉन्फ़िगरेशन फ़ाइल पढ़ना—अक्सर आप arrays के अंदर arrays के साथ समाप्त होंगे। flat() उन परतों को एक एकल रैखिक सूची (single linear list) में समेटने के लिए मौजूद है।
एक नेस्टेड array पर flat() कॉल करें और यह डिफ़ॉल्ट रूप से एक स्तर की गहराई तक फ्लैट करता है:
const nested = [[1, 2], [3, 4]];
const flat = nested.flat();
// [1, 2, 3, 4]
मूल nested array बिल्कुल वैसा ही रहता है जैसा वह था। यदि आपकी नेस्टिंग अधिक गहरी है, तो flat(2) जैसा depth argument पास करें या flat(Infinity) का उपयोग करें जब आपको पता न हो कि कितनी परतें मौजूद हैं। यह हाथ से रिकर्सिव हेल्पर फ़ंक्शन लिखने से कहीं बेहतर है।
जब आपको तत्वों को बदलने (transform) और परिणाम को फ्लैट करने, दोनों की आवश्यकता हो, तो flatMap() एक ही बार में दोनों काम करता है। यह प्रत्येक तत्व पर एक मैपिंग फ़ंक्शन चलाता है, और फिर लौटाए गए arrays को एक स्तर तक फ्लैट कर देता है। अलग-अलग .map().flat() कॉल के साथ ऐसा करना काम तो करता है, लेकिन flatMap() बीच वाले array को छोड़ देता है, जिससे मेमोरी बचती है और दूसरा iteration भी नहीं करना पड़ता। इसका एक क्लासिक उदाहरण वाक्यों के array को व्यक्तिगत शब्दों में विभाजित करना है:
const lines = ['hello world', 'foo bar'];
const words = lines.flatMap(line => line.split(' '));
// ['hello', 'world', 'foo', 'bar']
Filtering और Mapping: अंतर को समझें
filter() एक नया array बनाता है जिसमें केवल वे आइटम होते हैं जो आपके टेस्ट को पास करते हैं। एलिमेंट को रखने के लिए कॉलबैक को एक truthy वैल्यू और उसे हटाने के लिए एक falsy वैल्यू लौटानी चाहिए। यह कभी भी सोर्स array को म्यूटेट (mutate) नहीं करता है।
const users = [
{ name: 'Ajay', active: true },
{ name: 'Bina', active: false }
];
const activeUsers = users.filter(u => u.active);
filter() को map() के साथ भ्रमित करना आसान है क्योंकि दोनों नए arrays लौटाते हैं और कॉलबैक स्वीकार करते हैं। अंतर उनके काम करने के तरीके (contract) में है। map() उसी लंबाई (same length) का एक नया array लौटाता है। हर तत्व को बदलें, एक कंडीशनल चेक चलाएं, और आप वास्तव में हर स्लॉट के लिए true या false देखेंगे, क्योंकि map() कभी भी आइटम को हटाता नहीं है—यह केवल उन्हें बदलता है। दूसरी ओर, filter() एक संभावित रूप से छोटा array लौटाता है जिसमें केवल वे तत्व होते हैं जो आपके टेस्ट को पास करते हैं। यदि आप डेटा हटाना चाहते हैं, तो filter() का उपयोग करें। यदि आप गिनती (count) को बनाए रखते हुए डेटा के हर हिस्से को बदलना चाहते हैं, तो map() का उपयोग करें।
reduce() के साथ Arrays को समेटना (Collapsing)
जहाँ map() और filter() arrays लौटाते हैं, वहीं reduce() पूरी सूची को एक एकल मान (value) में बदल देता है। यह बाएं से दाएं चलता है, और प्रत्येक चरण में एक accumulator को साथ लेकर चलता है।
const totals = [10, 20, 30];
const sum = totals.reduce((acc, current) => acc + current, 0);
// 60
दूसरा argument—ऊपर दिए गए उदाहरण में 0—प्रारंभिक accumulator वैल्यू है। यदि आप इसे छोड़ देते हैं, तो JavaScript पहले तत्व को शुरुआती बिंदु के रूप में उपयोग करता है, और index 1 से iteration शुरू करता है। यह लोगों को तब उलझा देता है जब array खाली हो, क्योंकि खाली array पर बिना किसी प्रारंभिक वैल्यू के reduce() कॉल करने से TypeError आता है।
Reduce केवल अंकगणित (arithmetic) के लिए नहीं है। आप इसका उपयोग फ्रीक्वेंसी टेबल बनाने, ऑब्जेक्ट्स को मर्ज करने, अधिकतम मान खोजने, या डेटा को कई रूपांतरणों (transformations) के माध्यम से भेजने के लिए कर सकते हैं। क्योंकि यह बहुत लचीला है, इसलिए कॉलबैक को छोटा रखना और अपने वेरिएबल्स को स्पष्ट रूप से नाम देना फायदेमंद होता है। एक घना reduce() ब्लॉक जल्दी ही उस लूप की तुलना में पढ़ने में कठिन हो जाता है जिसे उसने बदला है।
Boolean Queries: every() और some()
कभी-कभी आपको एक नए array की आवश्यकता नहीं होती; आपको संग्रह (collection) के बारे में हाँ या ना में उत्तर चाहिए होता है। every() केवल तभी true लौटाता है जब सभी तत्व आपकी शर्त को पूरा करते हैं। यदि पहला तत्व ही विफल हो जाता है, तो यह तुरंत रुक जाता है और false लौटाता है।
const scores = [85, 92, 78];
const allPassed = scores.every(s => s >= 50);
some() इसका ठीक उल्टा है। यह true लौटाता है यदि कम से कम एक तत्व टेस्ट पास कर लेता है। जैसे ही कोई मैच मिलता है, यह तुरंत रुक जाता है।
const hasPerfectScore = scores.some(s => s === 100);
ये मेथड वैलिडेशन चेक के लिए मैन्युअल लूप की जगह लेते हैं। क्या आपको यह पुष्टि करने की आवश्यकता है कि प्रत्येक फ़ॉर्म फ़ील्ड भरा गया है? every() का उपयोग करें। क्या आपको यह जानने की आवश्यकता है कि क्या किसी उपयोगकर्ता के पास एडमिन अधिकार हैं? some() का उपयोग करें।
Mutation के बिना निरीक्षण (Inspecting) और अपडेट करना
आधुनिक JavaScript इम्यूटेबिलिटी (immutability) पर बहुत अधिक जोर देती है। ये मेथड आपको मूल डेटा को छुए बिना arrays का निरीक्षण करने या उन्हें अपडेट करने में मदद करते हैं।
keys() एक iterator object लौटाता है जिसमें array के indices होते हैं। entries() [index, value] जोड़ों का एक iterator लौटाता है। जब आप साफ डेस्ट्रक्चरिंग (destructuring) चाहते हैं, तो for...of लूप में ये बहुत उपयोगी होते हैं:
const fruits = ['apple', 'banana'];
for (const [index, fruit] of fruits.entries()) {
console.log(index, fruit);
}
with() भाषा में एक नया जुड़ाव है जो एक विशिष्ट index पर एक element को बदल देता है और एक बिल्कुल नया array लौटाता है। यह bracket assignment—arr[index] = value—की समान आवश्यकता को पूरा करता है, सिवाय इसके कि यह source array को बिना बदले छोड़ देता है। React जैसे frameworks में, जहाँ direct mutation, change detection को तोड़ देता है, with() एक सुरक्षित रास्ता है:
const original = [10, 20, 30];
const updated = original.with(1, 99);
// original: [10, 20, 30]
// updated: [10, 99, 30]
Spread और Rest: ... की दो दिशाएँ
Spread syntax और rest parameter दोनों में एक ही तीन डॉट्स का उपयोग होता है, लेकिन वे डेटा को विपरीत दिशाओं में ले जाते हैं।
एक array को individual elements में विस्तारित करने के लिए spread operator का उपयोग करें। यह arrays को merge करने या उन्हें clone करने का सबसे साफ तरीका है:
const front = [1, 2];
const back = [3, 4];
const merged = [...front, ...back];
const copy = [...merged];
आप आसानी से अतिरिक्त items भी जोड़ सकते हैं: [0, ...front, 99, ...back]।
rest parameter कई elements को एक single array में इकट्ठा करता है। Function signature के अंदर, यह बचे हुए सभी arguments को इकट्ठा कर लेता है:
function logFirst(first, ...rest) {
console.log('First:', first);
console.log('Rest:', rest);
}
logFirst('a', 'b', 'c');
// First: a
// Rest: ['b', 'c']
साथ मिलकर वे variadic input स्वीकार करने, उसे array के रूप में process करने, और फिर उसे function calls या नए data structures में वापस फैलाने के सामान्य कार्य को संभालते हैं।
मुख्य निष्कर्ष
इन methods का लक्ष्य one-liners से साथियों को प्रभावित करना नहीं है। इसका लक्ष्य आपके intent को स्पष्ट करना है। जब कोई अन्य developer filter() देखता है, तो वे जानते हैं कि आप items हटा रहे हैं। जब वे reduce() देखते हैं, तो वे जानते हैं कि आप data को collapse कर रहे हैं। जब वे with() देखते हैं, तो वे जानते हैं कि original array कहीं न कहीं सुरक्षित है। इन tools में महारत हासिल करने का अर्थ है ऐसा code लिखना जो स्वयं को स्पष्ट करे, और यह किसी भी clever loop की तुलना में कहीं अधिक समय बचाता है।
