0

I have 2 dim array, which looks like this:

var a = [[1,2,3],[4,5,6],[7,8,9]];

I want to write an iterator which will return one value a time when it's called.

iterator(); //returns 1
iterator(); //returns 2
iterator(); //returns 3

I tried such approach:

function iterator() {
    var a = [[1,2,3],[4,5,6],[7,8,9]];
    var i, j;
    return function() {
        for (var i = 0; i < a.length; i++) {
            var b = a[i];
            for (var j = 0; j < b.length; j++) {
                return a[i][j];
            }
        }
    }
};
var a = iterator();
a(); //1
a(); //1
a(); //1

It always returns me first element.

I can try this one:

function iterator() {
    var a = [[1,2,3],[4,5,6],[7,8,9]];
    var i = 0, j = 0;
    return function() {
        for (; i < a.length; i++) {
            var b = a[i];
            for (; j < b.length; j++) {
                return a[i][j];
            }
        }
    }
};

Also not works.

But if I try this one:

function test() {
    var a = [1,2,3,4,5], i = 0;
    return function() {
        while (i < a.length) {
            return a[i++];
        }
    }
}
var a = test();
a(); //1
a(); //2
a(); //3

It works fine.

What is the difference here? How to make for loop work?

One other visible problem for me is bounds. How should I stop when I reach array bounds?

8
  • you initialize i=0 , j=0 with the for loop each time you call the function.. Commented Oct 6, 2015 at 11:34
  • @Hacketo made an edit, check my second try for iterator. also won't work. Commented Oct 6, 2015 at 11:36
  • because you never increment i and j, it execute the return statement before Commented Oct 6, 2015 at 11:37
  • why don't you just collapse the array? Commented Oct 6, 2015 at 11:39
  • @epascarello I'm using it as 2d structure, to fill grid. Commented Oct 6, 2015 at 11:42

4 Answers 4

1

Instead of using an inner for-loop for the second dimension you can use a simple if to test the bounds of j

function iterator() {
    var a = [[1,2,3],[4,5,6],[7,8,9]];
    var i = 0, j = 0;
    return function() {
        for (; i < a.length; i++) {
            var b = a[i];
            if (j < b.length){
                return a[i][j++];
            }
            j = 0;
        }
        return undefined; // reached when there is no value left
    }
};
Sign up to request clarification or add additional context in comments.

2 Comments

Actually the loops are not necessary. You could just return a[i][j] or undefined.
@AdrianMaire the for-loop is only here to simplify the bounds check of i and i incrementation (save one line .) And I added return undefined because OP might want to return something else than undefined
1

Why do you need a loop at all in this case. You are effectively flattening the array anyway. You could just increment the indices along with a bounds check:

function iterator() {
    var a = [[1,2,3],[4,5,6],[7,8,9]], i = 0, j = 0;
    return function() {
        if (j >= a[i].length) { j = 0; i++; }
        if (i >= a.length) { j = 0; i = 0; }
        snippet.log( a[i][j++] );
    }
};
var x = iterator();
x(); x(); x(); x(); x(); x(); x(); x(); x(); x(); 
x(); x(); x(); x(); x(); x(); x(); x(); x(); x();
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Comments

1

It is generally not a good idea to extend standard feature, but for a didactical example I will make an exception. For real use, I suggest to implement your own class.

General Idea

Array.prototype.beginIterator = function()
{
    var counter = 0;
    return function()
        { 
            if (counter<=this.length) return this[counter++]; 
            else return undefined;
        };
}

Then you could iterate like follow:

var a = [3,1,4,1,5];
var it = a.beginIterator();
for (var i=it(); i!=undefined; i=it())
{
    alert(i);
}

This at the moment work only with single-dimension array, but it could be applied with any logic to other arrays or object.

Multi-dimensional (Any) solution:

The following iterator allow any-dimension array in any combination:

Array.prototype.beginIterator = function()
{
    var counter = 0;
    var iterators = null;

    return function()
        { 
            val = undefined;
            if (iterators!=null)
            {
                val = iterators();
                if (val!==undefined) return val;
                else
                {
                    iterators = null;
                    counter++;
                }
            }

            while (counter <=this.length)
            {
                if (!(this[counter] instanceof Array)) return this[counter++];
                else
                {
                    iterators = this[counter++].beginIterator();
                    val = iterators();
                    if (val!==undefined) return val;
                }
            }
            return undefiend;
        };
}

Example of use:

var a = [3,[3,5,7],4,[1,[2,5,8]],5];
var it = a.beginIterator();
for (var i=it(); i!=undefined; i=it())
{
    alert(i);
}

Comments

0

Iterators implement a specific protocol. To make it work on a 2D array with a compliant iterator, you can make use of the iterator helper method flatMap which was introduced with ECMAScript 2025 (not to be confused with the array method bearing the same name):

const a = [[1,2,3],[4,5,6],[7,8,9]];
const it = a.values().flatMap(Object);

console.log(it.next().value); // 1
console.log(it.next().value); // 2
console.log(it.next().value); // 3
console.log(it.next().value); // 4

Object is passed as argument just to get an identity mapping. It could also be x => x. The goal of that callback is to return an iterable. Since the nested array is iterable, we can just return it as-is.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.