एक पुनरावृत्ति पर मानचित्र () का उपयोग करना


92

मान लें कि हमारे पास एक मानचित्र है: रिटर्न let m = new Map();का उपयोग करके m.values()एक मानचित्र पुनरावृत्ति।

लेकिन मैं उस पुनरावृत्त पर forEach()या map()उस समय का उपयोग नहीं कर सकता और उस पुनरावृत्ति पर थोड़ी देर के लूप को लागू करना ईएस 6 प्रस्ताव कार्यों की तरह विरोधी पैटर्न की तरह लगता है map()

तो क्या map()एक पुनरावृत्ति पर उपयोग करने का एक तरीका है?


बॉक्स से बाहर नहीं है, लेकिन आप lodash mapफ़ंक्शन जैसे तीसरे पक्ष के पुस्तकालयों का उपयोग कर सकते हैं जो मानचित्र का समर्थन करता है।
खतरनाक

मानचित्र में अपने प्रमुख-मूल्य जोड़े पर पुनरावृत्ति के लिए एक forEach है
खतरनाक

एटरेटर को एक सरणी में बदलना और उस पर मैप करना जैसे Array.from(m.values()).map(...)काम करता है, लेकिन मुझे लगता है कि यह ऐसा करने का सबसे अच्छा तरीका नहीं है।
जिमिनप

एक सरणी का उपयोग करते हुए आप किस समस्या को हल करना चाहते हैं जबकि एक सरणी का उपयोग करने के लिए बेहतर होगा Array#map?
नीना शोल्ज़

1
@NinaScholz मैं यहाँ एक सामान्य सेट का उपयोग कर रहा हूँ: stackoverflow.com/a/29783624/4279201
shinzou

जवाबों:


84

ऐसा करने का सबसे सरल और सबसे कम तरीका है:

Array.from(m).map(([key,value]) => /* whatever */)

और भी बेहतर

Array.from(m, ([key, value]) => /* whatever */))

Array.fromकिसी भी चलने योग्य या सरणी जैसी चीज़ को लेता है और इसे एक सरणी में परिवर्तित करता है! जैसा कि डैनियल टिप्पणियों में बताते हैं, हम एक पुनरावृत्ति को हटाने के लिए रूपांतरण और बाद में एक मध्यवर्ती सरणी में एक मैपिंग फ़ंक्शन जोड़ सकते हैं।

का उपयोग Array.fromकर अपने प्रदर्शन से O(1)के O(n)रूप में @hraban टिप्पणियों में बताते हैं। चूंकि mएक है Map, और वे अनंत नहीं हो सकते हैं, हमें अनंत अनुक्रम के बारे में चिंता करने की आवश्यकता नहीं है। अधिकांश उदाहरणों के लिए, यह पर्याप्त होगा।

मानचित्र के माध्यम से लूप करने के अन्य तरीके हैं।

का उपयोग करते हुए forEach

m.forEach((value,key) => /* stuff */ )

का उपयोग करते हुए for..of

var myMap = new Map();
myMap.set(0, 'zero');
myMap.set(1, 'one');
for (var [key, value] of myMap) {
  console.log(key + ' = ' + value);
}
// 0 = zero
// 1 = one

क्या मानचित्रों की अनंत लंबाई हो सकती है?
ktilcu

2
@ktilcu एक सूचना के लिए: हाँ। एक इटमैटर पर एक .map को जनरेटर पर एक परिवर्तन के रूप में माना जा सकता है, जो एक इट्रेटर को स्वयं लौटाता है। एक तत्व को पॉप करने से अंतर्निहित पुनरावृत्ति को कॉल किया जाता है, तत्व को रूपांतरित करता है, और वापस लौटाता है।
हरबन

8
इस उत्तर के साथ समस्या यह है कि यह एक O (n) एक में O (1) मेमोरी एल्गोरिथ्म हो सकता है, जो बड़े डेटासेट के लिए काफी गंभीर है। इसके अलावा, निश्चित रूप से, परिमित, गैर-सुव्यवस्थित पुनरावृत्तियों की आवश्यकता होती है। प्रश्न का शीर्षक "इट्रेटर पर मानचित्र () का उपयोग करना" है, मैं असहमत हूं कि आलसी और अनंत अनुक्रम प्रश्न का हिस्सा नहीं हैं। यह ठीक है कि लोग पुनरावृत्तियों का उपयोग कैसे करते हैं। "मानचित्र" केवल एक उदाहरण ("कहो ..") था। इस उत्तर के बारे में अच्छी बात इसकी सादगी है, जो बहुत महत्वपूर्ण है।
१५:५५ बजे हब्रन

1
@ प्रभु इस चर्चा में जोड़ने के लिए धन्यवाद। मैं कुछ कैविएट को शामिल करने के उत्तर को अपडेट कर सकता हूं ताकि भविष्य के यात्रियों के सामने जानकारी और केंद्र हो। जब यह नीचे आता है तो हमें अक्सर सरल और इष्टतम प्रदर्शन के बीच निर्णय करना होगा। मैं आमतौर पर प्रदर्शन पर सरल (डिबग करने, बनाए रखने, समझाने) के साथ होगा।
ktilcu

3
@ktilcu आप इसके बजाय कॉल कर सकते हैं Array.from(m, ([key,value]) => /* whatever */)(ध्यान दें कि मैपिंग फ़ंक्शन अंदर है from) और फिर कोई मध्यवर्ती सरणी नहीं बनाई गई है ( स्रोत )। यह अभी भी O (1) से O (n) तक जाता है, लेकिन कम से कम पुनरावृत्ति और मानचित्रण केवल एक पूर्ण पुनरावृत्ति में होता है।
डैनियल

19

आप इस पर लूप करने के लिए एक और पुनरावृत्ति फ़ंक्शन को परिभाषित कर सकते हैं:

function* generator() {
    for (let i = 0; i < 10; i++) {
        console.log(i);
        yield i;
    }
}

function* mapIterator(iterator, mapping) {
    for (let i of iterator) {
        yield mapping(i);
    }
}

let values = generator();
let mapped = mapIterator(values, (i) => {
    let result = i*2;
    console.log(`x2 = ${result}`);
    return result;
});

console.log('The values will be generated right now.');
console.log(Array.from(mapped).join(','));

अब आप पूछ सकते हैं: क्यों नहीं Array.fromइसके बजाय का उपयोग करें ? क्योंकि यह पूरे पुनरावृत्त के माध्यम से चलेगा, इसे एक (अस्थायी) सरणी में सहेजें, इसे फिर से पुनरावृत्त करें और फिर मैपिंग करें। यदि सूची बहुत बड़ी है (या संभावित रूप से अनंत) तो यह अनावश्यक मेमोरी उपयोग को बढ़ावा देगा।

बेशक, यदि वस्तुओं की सूची काफी छोटी है, तो उपयोग Array.fromपर्याप्त से अधिक होना चाहिए।


स्मृति की एक सीमित मात्रा में अनंत डेटा संरचना कैसे हो सकती है?
शिंज़ौ

3
यह नहीं है, यह बात है। इसका उपयोग करके आप इट्रेटर स्रोत का एक समूह का रूपांतरण करके इटर्मीटर स्रोत का अंत करके "डेटा स्ट्रीम" बना सकते हैं और अंत में उपभोक्ता सिंक कर सकते हैं। उदाहरण के लिए ऑडियो प्रोसेसिंग स्ट्रीमिंग, विशाल फाइलों के साथ काम करना, डेटाबेस पर एग्रीगेटर्स, आदि
hraban

1
मुझे यह उत्तर पसंद है। क्या कोई लाइब्रेरी की सिफारिश कर सकता है जो एटरेबेल जैसी विधियों को पुनरावृत्तियों पर प्रस्तुत करती है?
जोएल मालोन

1
mapIterator()iterator.return()जब तक कि रिटर्न वैल्यूज़ के अगले को कम से कम एक बार कॉल नहीं किया जाता है, तब तक यह गारंटी नहीं दी जाती है कि अंतर्निहित इटरेटर को ठीक से बंद ( बुलाया) किया जाएगा । देखें: repeater.js.org/docs/safety
Jaka Jančar

क्यों आप मैन्युअल रूप से इटर्मा प्रोटोकॉल का उपयोग सिर्फ एक के बजाय कर रहे हैं for .. of .. loop?
काउलिक्स

11

यह सबसे सरल और सबसे अच्छा तरीका है कि Array.fromइसे प्राप्त करने के लिए दूसरे तर्क का उपयोग करें:

const map = new Map()
map.set('a', 1)
map.set('b', 2)

Array.from(map, ([key, value]) => `${key}:${value}`)
// ['a:1', 'b:2']

यह दृष्टिकोण किसी भी गैर-अनंत पुनरावृत्ति के लिए काम करता है । और यह एक अलग कॉल का उपयोग करने से बचता है, Array.from(map).map(...)जो पुनरावृत्ति के माध्यम से दो बार पुनरावृत्ति करेगा और प्रदर्शन के लिए बदतर होगा।


3

आप पुनरावृत्त पर एक पुनरावृत्ति प्राप्त कर सकते हैं, फिर एक और पुनरावृत्ति लौटाते हैं जो प्रत्येक पुनरावृत्त तत्व पर मैपिंग कॉलबैक फ़ंक्शन को कॉल करता है।

const map = (iterable, callback) => {
  return {
    [Symbol.iterator]() {
      const iterator = iterable[Symbol.iterator]();
      return {
        next() {
          const r = iterator.next();
          if (r.done)
            return r;
          else {
            return {
              value: callback(r.value),
              done: false,
            };
          }
        }
      }
    }
  }
};

// Arrays are iterable
console.log(...map([0, 1, 2, 3, 4], (num) => 2 * num)); // 0 2 4 6 8

2

आप itiriri का उपयोग कर सकते हैं जो iterables के लिए सरणी जैसी विधियों को लागू करता है:

import { query } from 'itiriri';

let m = new Map();
// set map ...

query(m).filter([k, v] => k < 10).forEach([k, v] => console.log(v));
let arr = query(m.values()).map(v => v * 10).toArray();

अच्छा! जेएस के एपीआई को ऐसा करना चाहिए था। हमेशा की तरह, रस्ट इसे सही हो जाता है: doc.rust-lang.org/std/iter/trait.Iterator.html
उड़ती हुई भेड़

"हमेशा की तरह, रस्ट सही हो जाता है" निश्चित ... इटैलर इंटरफ़ेस के लिए सभी प्रकार के सहायक कार्यों के लिए एक मानकीकरण प्रस्ताव है github.com/tc39/proposal-iterator-helpers आप fromfn से आयात करके कोरजेंट के साथ आज इसका उपयोग कर सकते हैं । "कोर-जेएस-प्योर / फीचर्स / इटरेटर" जो "नया" इटरेटर लौटाता है।
chpio 12

2

पर एक नज़र डालें Https://www.npmjs.com/package/fluent-iterable

सभी iterables के साथ काम करता है (नक्शा, जनरेटर समारोह, सरणी) और async iterables।

const map = new Map();
...
console.log(fluent(map).filter(..).map(..));

1

एक प्रस्ताव है, जो कई सहायक कार्य ला रहा है Iterator: https://github.com/tc39/proposal-iterator-helpers ( प्रस्तुत किया गया )

आप इसका आज उपयोग करके उपयोग कर सकते हैं core-js:

import { from as iterFrom } from "core-js-pure/features/iterator";

// or if it's working for you:
// import iterFrom from "core-js-pure/features/iterator/from";

let m = new Map();

m.set("13", 37);
m.set("42", 42);

const arr = iterFrom(m.values())
  .map((val) => val * 2)
  .toArray();

// prints "[74, 84]"
console.log(arr);

0

यहाँ अन्य उत्तर हैं ... अजीब। वे पुनरावृत्ति प्रोटोकॉल के कुछ हिस्सों को फिर से लागू कर रहे हैं। आप ऐसा कर सकते हैं:

function* mapIter(iterable, callback) {
  for (let x of iterable) {
    yield callback(x);
  }
}

और यदि आप एक ठोस परिणाम चाहते हैं तो बस प्रसार ऑपरेटर का उपयोग करें ...

[...iterMap([1, 2, 3], x => x**2)]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.