Javascript: How to implement iterator over multidimensional array? - javascript

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?

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
}
};

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>

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);
}

Related

How to assign a function to a javascript variable

I have a requirement where I need to access an array element using its function.
For example, I have Array A[], now I want to create array B, such that
A[i] === B[i].value()
I tried below code but I am getting error as B[i].value is not a function
<script>
function test(A) {
var B = new Array();
for(var i=0; i<A.length; i++) {
B[i] = function value() {
return A[i];
};
}
for(var i=0; i< B.length; i++) {
console.log(B[i].value());
}
return B;
}
A=[1,2,3];
B = test(A);
</script>
What is the correct way for this?
You need to assign an object instead:
B[i] = {
value: function () {
return A[i];
}
}
To avoid any problems with the scope of i, you can use the statement let
The let statement declares a block scope local variable, optionally initializing it to a value.
function test(A) {
var B = new Array();
for (let i = 0; i < A.length; i++) {
B[i] = {
value: function() {
return A[i];
}
};
}
for (let k = 0; k < B.length; k++) {
console.log(B[k].value());
}
return B;
}
var B = test([1, 2, 3]);
console.log(B)
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could make value anonymous e.g. B[i] = function () { /* Your code */ } then just call B[i]() instead of B[i].value()

Javascript pass counter to function

I know how do closures work, but it is not very clear to me.
How does the below snippet works under the hood (output's 0) :
function fillFunctionArr() {
let arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = {
inner: i,
innerFunc: function() {
console.log(this.inner)
}
}
}
return arr;
}
var ex = fillFunctionArr();
ex[0].innerFunc()
Sure, if it were declared as :
arr[i] = {
// inner: i,
innerFunc: function() {
console.log(i)
}
}
The output will be 10.
Why is the first snippet more preferable than the below one:
function fillFunctionArr() {
let arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = (function(qnt) {
return function() {
console.log(qnt)
}
})(i);
}
return arr;
}
var ex = fillFunctionArr();
ex[0]()
There is no 'preferable' choice. The choice depends on logic of outer code, that you want to extend. Both initialized immediately on cycle iteration. It's just one of the edges of JS flexibility.

Pass by reference?

Can anyone tell me why all the object.num's print as 1? This is driving me mad. Somehow after the for loop the values of the object.num = 1 no matter what, even though they are never set to 1. Please copy the entire segment to debug.
<script type="text/javascript">
window.addEventListener("load", main, false);
const n = 4;
function main()
{
var belt = new Array(4*n);
initArr(belt);
printIt(belt);
populateArr(belt);
printIt(belt);
reorder(belt);
printIt(belt);
}
function populateArr(arr)
{
var a = {name:"a", num:0};
var b = {name:"b", num:0};
var end = arr.length;
var i = end-1;
for(var temp = n; temp > 0; temp--)
{
a.num = temp;
arr[i] = a;
i-=2;
}
i = end-2;
for(var temp = n; temp > 0; temp--)
{
b.num = temp;
arr[i] = b;
i-=2;
}
return arr;
}
function printIt(arr)
{
var tempArr = new Array(arr.length);
for(var i=0; i < arr.length; i++)
{
tempArr[i] = arr[i].name + arr[i].num;
}
console.log(tempArr);
}
function initArr(arr)
{
var nothing = {name:null, num:0};
for(var i=0; i<arr.length; i++)
{
arr[i] = nothing;
}
return arr;
}
function reorder(arr)
{
var nothing = {name:null, num:0};
var counter = 0;
var aIndex = 0;
var bIndex = null;
for(var i=0; i < arr.length; i++)
{
if(arr[i].name === "b" && bIndex === null)//first b doesn't get moved
{
bIndex = i+1;
}
else if(arr[i].name === "a")
{
arr[aIndex] = arr[i];
arr[i] = nothing;
counter++;
aIndex++;
}
else if(arr[i].name ==="b")
{
arr[bIndex] = arr[i];
arr[i] = nothing;
counter++;
bIndex++;
}
}
console.log("count: " + counter);
console.log("n: " + n);
return arr;
}
</script>
Somehow after the for loop the values of the object.num = 1 no matter what, even though they are never set to 1.
Yes "they" are - "they're" set to 1 in the last iteration of this loop:
for(var temp = n; temp > 0; temp--)
{
a.num = temp;
arr[i] = a;
i-=2;
}
The last iteration of that loop is when temp is 1.
Now, you've only actually got one object - and you're setting every element of the array to be a reference to that object. That's why all the values in the array look the same. If you want to create a different object each time, you should use:
for(var temp = n; temp > 0; temp--)
{
arr[i] = { name: "a", num: temp };
i -= 2;
}

JS array string patch name to array(object)

i have trouble with js array.
I have patch name(string) look like ( "user.joey.friends.0.franc" ) and i need to "compress" to look like this:
console.log( parseArray( "user.joey.friends.0.franc" ) );
//Object {
user: {
joey: {
friends: {
0: {
franc: 1
}
}
}
}
}
Any ideas, how to do that ?
Here's a non recursive way to get you most of the way there.
function compress(str) {
var split = str.split('.'), obj = {}, current = obj, i;
for (i = 0; i < split.length; i++){
current[split[i]] = {} ;
current = current[split[i]];
}
return obj;
}
If you're saying you have an object with the structure you've quoted, and you're trying to look up the property defined by your string "user.joey.friends.0.franc", that's a fairly straightforward loop: Live Example
function findProperty(obj, path) {
var steps = path.split(/\./);
var step;
var n;
for (n = 0; n < steps.length; ++n) {
step = steps[n];
if (!(step in obj)) {
return undefined;
}
obj = obj[step];
}
return obj;
}
var a = {
user: {
joey: {
friends: {
0: {
franc: 1
}
}
}
}
};
console.log(findProperty(a, "user.joey.friends.0.franc")); // "1"
If you're trying to create an object from a string, this solution is what I currently use until I find something better (I believe it based on a namespacing method from Yahoo):
function createObject() {
var a = arguments, i, j, d, _this;
var out = {};
for (i = 0; i < a.length; i = i + 1) {
d = a[i].split('.');
out2 = out;
for (j = 0; j < d.length; j = j + 1) {
out2[d[j]] = out2[d[j]] || {};
out2 = out2[d[j]];
}
}
return out;
}
createObject('user.joey.friends.0.franc');
DEMO

Closures in Javascript: assigning local variable doesn't work

Just when I thought I understood closures...
The following code snippet:
function f() {
var a = [];
var i;
for (i = 0; i < 3; i++) {
a[i] = function () {
var x = i;
return x;
}
}
return a;
}
var a = f();
console.log(a[0]());
console.log(a[1]());
console.log(a[2]());
prints out 3, 3, 3. I don't understand why. I'm copying the value of 'i' to the local variable x, so there should be three x's: x0=0, x1=1. x2=2. How are all of them reading the final value of i?
Your problem is caused by each a[i] being, in fact, a closure. They all share the same i, which is evaluated when each a[i] is called, not when the loop executes. You need to create each closure with a separate context. For instance:
function f() {
var a = [];
var i;
for (i = 0; i < 3; i++) {
a[i] = makeClosure(i);
}
return a;
}
function makeClosure(i) {
return function () {
var x = i;
return x;
}
}
Even though the value of i changes in your for loop, it's still the same i variable. You need to shadow i in that scope and effectively pass it by value:
for (var i = 0; i < 3; i++) {
(function(x) {
a[x] = function() {
return x;
}
})(i);
}

Categories

Resources