XCS
XCS

Reputation: 28157

JavaScript start iterating Map() from specific element/index

I know that elements in a Map can be iterated over, in the order of insertion.

Let's assume we have this map:

const a = new Map();
a.set('x', 5);
a.set('y', 10);
a.set('z', 5);

And we want to find first element in a with the value 5 and then the next element with same value of 5.

// el will be 5, 10, 5...
for(const el of a) {
  if(el === 0) {
    // How can I iteratate over `a` starting from index(el) + 1
    for (??) {}
  }    
}

If I was using an Array instead we could do something like (ignoring the keys):

const a = new Array(5, 10, 5);
for(let i = 0; i < a.length; ++i) {
  if(a[i] === 5) {
    // Here I can start iterating from i + 1
    for(let j = i + 1; j < a.length; ++j) {
         a[j] === 5 && console.log('FOUND!');
    }
  }
}

I am not very familiar with iterators, but I think it should be somehow possible to start iterating from a specific element in the map.

const x = a.get('x');
 // iterate over Map `a` starting from the element that comes after x

One solution, that I am not particularly happy about, is to get a copy of the keys or entries each time we perform the operation const elements = a.entries(), so then we could quickly iterate over it, but it uses a lot of extra memory.

Upvotes: 3

Views: 2965

Answers (2)

Keith
Keith

Reputation: 24221

You could use a generators to do this,..

One advantage is that you can short circuit generators with a break..

Example below..

const a = new Map();
a.set('x', 5);
a.set('y', 10);
a.set('z', 5);

a.set('a', 10);
a.set('b', 5);

function* findFirstThenNext(m, v) {
  let ix = 0; 
  for (const mm of m) {
    if (v === mm[1]) {
      yield {ix, key:mm[0]};
    }
    ix += 1;
  }
}

let count = 0;
for (const ret of findFirstThenNext(a, 5)) {
  console.log(`Found @${ret.ix} with key ${ret.key}`);
  count ++;
  if (count >= 2) break;
}

Using a mix of the for loop and iterators, you could create simple list, then do your double for loops using iterators.

The nice thing here, is if you use this sort of for looping in lots of places, then the makeOuterInnerIter function can be re-used for any iterable.

const a = new Map();
a.set('x', 5);
a.set('y', 10);
a.set('z', 5);

function* makeOuterInnerIter(iter) {
  const stack = Array.from(iter);
  for (let ol = 0; ol < stack.length; ol += 1) {
    yield {
      value: stack[ol],
      inner: (function *inner() {
        for (let il = ol + 1; il < stack.length; il += 1) yield stack[il];
      })()
    };
  }
}


for (const {value: [okey, ovalue], inner} of makeOuterInnerIter(a)) {
  console.log(`outer: ${okey}: ${ovalue}`);
  for (const [ikey, ivalue] of inner) {
    console.log(`  inner: ${ikey}: ${ivalue}`);
  }
}

Upvotes: 2

mgabor6
mgabor6

Reputation: 541

Another approach with generators:

function* itWrapper(iterator, value) {
    let found = false;
    for(let item of iterator) {
        if(item[1] == value) {
            if(found) {
                yield 'FOUND!'; // or: yield item[1];
            } else {
                found = true;
            }
        }
    }
}

And then use like this:

for(let item of itWrapper(a[Symbol.iterator](), 5)) {
    console.log(item);
}

Upvotes: 0

Related Questions