33

I have an array with alot of items, and I am creating a list of them. I was thinking of paginating the list. I wonder how can I start a forEach or for loop at some index in an array, that would be in my example the number of items in the list on each page, so that I don't need to iterate over the whole array in each loop?

arr.forEach(function (item) {
  someFn(item);
})


for (var i = 0, len = arr.length; i < len; i++) {
  someFn(arr[i]);
}
1
  • why don't you slice() your Array to get the Items on the curent page var offset = currentPage*itemsPerPage; arr.slice(offset, offset+itemsPerPage).forEach(someFn); Commented Apr 8, 2017 at 15:34

6 Answers 6

24

You could use a copy of the array, by using Array#slice

The slice() method returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified.

array.slice(10, 20).forEach(someFn); // only for functions which respects API of forEach*

* parameters for a callback

Or you can start at a given index and end at a given index.

for (var i = 10, len = Math.min(20, arr.length); i < len; i++) {
    someFn(arr[i]);
}

With

Math.min(20, arr.length)

returns a value, if the array is smaller than the given value 20. For example if the array has only index 0 ... 14, you get as result 15.

Sign up to request clarification or add additional context in comments.

4 Comments

@Leff: Beware that if someFn allows multiple arguments, there's a big difference between your callback forEach(function(item) { someFn(item); }) and Nina's forEach(someFn) above, because forEach passes multiple arguments to the function it calls. But if someFn only ever uses its first argument, Nina's is the way to go.
Bounds checks should be included on the i and len for the for loop. If the OP didn't understand how to start and stop an indexed loop where desired, then they likely wouldn't think of that either.
@squint, for i, i think it's not necessary, because the length check prevents from iterating non existent items.
Yeah, that's a good point. Only exception would be if i ends up being negative, but that's probably not too likely.
5

Unfortunately Array#forEach iterates over every element in the given array, but you could apply a simple condition to determine to which elements (with specified index) apply the given function.

i > 3 ? someFn(item) : null;
^ if index more than 3 - call the function

var arr = [1,2,3,4,5,6,7];

function someFn(elem){
  console.log(elem);
}

arr.forEach(function(item, i) {
  return i > 3 ? someFn(item) : null;
})

Comments

5

forEach doesn't offer that feature, no. So your choices are:

  1. A simple for loop

  2. Ignoring the indexes you don't want to handle (as in Kind user's answer)

  3. Using slice (as in Nina's answer)

  4. Writing your own function

Here's #4 as an Array.prototype extension (non-enumerable, of course; adding enumerable properties to Array.prototype breaks a lot of code); after it is a standalone version for when adding to Array.prototype isn't appropriate:

// Giving ourselves the function
Object.defineProperty(Array.prototype, "myEach", {
  value: function(from, to, callback, thisArg) {
    if (typeof from === "function") {
      thisArg = callback;
      callback = to;
      to = from;
      from = 0;
    }
    if (typeof to === "function") {
      thisArg = callback;
      callback = to;
      to = this.length;
    }
    for (var n = from; n < to; ++n) {
      callback.call(thisArg, this[n], n, this);
    }
  }
});

// Using it:
var arr = ["zero", "one", "two", "three", "four", "five", "six", "seven"];
console.log("*** From 3:");
arr.myEach(3, function(e) { console.log(e); });
console.log("*** From 3 (inclusive) to 5 (exclusive):");
arr.myEach(3, 5, function(e) { console.log(e); });
console.log("*** All:");
arr.myEach(function(e) { console.log(e); });
console.log("*** Check thisArg handling on 0-2:");
var o = {answer: 42};
arr.myEach(0, 2, function(e) {
  console.log(e + " (this.answer = " + this.answer + ")");
}, o);
.as-console-wrapper {
  max-height: 100% !important;
}

Again note that that's a non-enumerable property, which is vital if you ever add anything to Array.prototype (otherwise, you break a lot of code).

You wouldn't do that in a library to be consumed by others, you'd just have a standalone function:

// Giving ourselves the function
function myEach(array, from, to, callback, thisArg) {
  if (typeof from === "function") {
    thisArg = callback;
    callback = to;
    to = from;
    from = 0;
  }
  if (typeof to === "function") {
    thisArg = callback;
    callback = to;
    to = array.length;
  }
  for (var n = from; n < to; ++n) {
    callback.call(thisArg, array[n], n, array);
  }
}

// Using it:
var arr = ["zero", "one", "two", "three", "four", "five", "six", "seven"];
console.log("*** From 3:");
myEach(arr, 3, function(e) {
  console.log(e);
});
console.log("*** From 3 (inclusive) to 5 (exclusive):");
myEach(arr, 3, 5, function(e) {
  console.log(e);
});
console.log("*** All:");
myEach(arr, function(e) {
  console.log(e);
});
console.log("*** Check thisArg handling on 0-2:");
var o = {answer: 42};
myEach(arr, 0, 2, function(e) {
  console.log(e + " (this.answer = " + this.answer + ")");
}, o);
.as-console-wrapper {
  max-height: 100% !important;
}

Comments

0

Thinking on what @NinaScholz commented, perhaps you can use variables and any changes would be set in those instead of changing the loop.

function someFn(item, array2){
  array2.push(item, array2);
}

var arrayItems1 = [1,2,3,4,5,6,7,8,9,10];
var arrayItems2 = [];

var firstIndex = 1;
var lastIndex = 5;
var i = 0;

for (i = firstIndex; i < lastIndex; i++){
     someFn(arrayItems1[i], arrayItems2);
}

alert(arrayItems2.join(' '));

Comments

0

You could apply some kind of implementation of the iterator pattern.

var Iterator = function (list, position) {
  return {
    isNext: function () {
      return position + 1 < list.length;
    },
    isDefined: function () {
      return (position < list.length && position >= 0);
    },
    element: function () {
      return list[position];
    },
    position: function () {
      return position;
    },
    moveNext: function () {
      if (this.isNext()) { return Iterator(list, position + 1); }
      return Iterator([], 0);
    }      
}

Iterator.forEach = function (action, iterator, length) {
  var counter = 0;
  while (counter < length && iterator.isDefined()) {
    counter = counter + 1;
    action(iterator.element(), iterator.position());
    iterator = iterator.moveNext();
  }

  return iterator;
}   

And then have an iterator to use for going over the list and keep the state of the last iteration over a list.

var list = [1, 3, 5, 3, 6];
var iterator = Iterator(list, 0);

iterator = Iterator.forEach(function (element, index) {
  console.log(element, index);
}, iterator, 3);

//1 0
//3 1
//5 2

Iterator.forEach(function (element, index) {
   console.log(element, index);              
}, iterator, 5);

//3 3
//6 4

Comments

-1

array.values() to get the iterator, .next() it and use it.

let ar=[1,2,3,4]

var _;for(let a of(_=ar.values(),_.next(),_)){
  console.log(a)
}

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.