2

I was looking for a way to add max/min functions to JavaScript's Array class, which seemed to be a solved problem: JavaScript: min & max Array values?. However, when I tried using that, I started getting errors from my code. It turns out that this approach doesn't work with loops.

for(i in myArray) { console.log(i) } 

prints out


1
2
3
max
min

Is there another approach I can use?

1
  • No, i does not. Consider reading on "working with arrays" Commented Feb 16, 2011 at 19:16

5 Answers 5

10

The accepted solution solves your immediate problem, but extending core objects is generally a bad idea. What happens when you include a library later that uses for..in? Or when you forget months later and use the wrong approach in a different section of your code?

Another option is to wrap and extend. Create a new type that uses an instance of Array as its prototype:

function ArrayThing() {}

ArrayThing.prototype = new Array();

Now you've got an object that you can extend without affecting Array:

ArrayThing.prototype.max = function() {
    return Math.max.apply(null, [].slice.call(this, 0))
}

ArrayThing.prototype.min = function() {
    return Math.min.apply(null, [].slice.call(this, 0))
}

var list = new ArrayThing();

// standard array methods still work
list.push(5);
list.push(22);
list.push(0);
list.push(-14);
list.length // => 4

// as do your new custom methods
list.max() // => 22
list.min() // => -14

This won't work in every situation, but unless you're sure you really, really need an Array, this is a useful alternative.

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

Comments

6

The for...in loop is used for looping through properties of an object. If you want to get the values from your array, you can do this:

for (var i = 0; i < myArray.length; i++)
{
    console.log(myArray[i])
}

4 Comments

also, object[string] vs. array[number]
Why not use i < like everyone else?
@missignno - no good reason (that was the way I had it in a local example), doing i < myArray.length as you suggest in your answer is actually cleaner, IMO.
You actually want to use for (var i = 0, I = myArray.length; i < I; i++) as this is still faster in most browsers; see blogs.oracle.com/greimer/resource/loop-test.html (caching length) and stackoverflow.com/questions/2742396/…
2

for in is a common Javascript trap. Instead of behaving like a foreach from other languages, it actualy enumerates all properties in a given object.

Since Javascript Arrays happen to have a property for each index using for in works sometimes, but as you have seen, it also enumerates any other properties you add. Another issue is that the for in is not guaranteed to go through the properties in any particular order, so your results can vary depending on which browser/runtime you use.

It is safer, then, to just use a boring for loop instead. There are many for loop idioms in Javascript, so I will list some:

Regular for loop:

for(i=0; i<arr.length; i++){

Regular loop, caching the length:

for(i=0, n=arr.length; i<n; i++){

Loops over arrays of objects/NodeLists:

for(i=0; obj=arr[i]; i++){ //this works as long as the array has no falsy values
    foo(obj)

Comments

1

You need to apply a check using hasOwnProperty. However this needs to applied wherever you are looping.

i.e:

for(i in myArray)
{
  if(arr.hasOwnProperty(i))
  {
   console.log(i);
  }
}

1 Comment

To me, this is such a hack-around. I refuse to add such code around all my for in loops. Therefore, I refuse to allow the use of any libraries that extend JS Object. Also, you shouldn't use for in to iterate over arrays if you expect it to be returned in order, the order of elements in for in is undefined. It currently works, but most new browsers are dropping that behavior in favor of performance gains
0

There's a more modern (IE9+) way to do this now:

var g = []; 

Object.defineProperty(g, "log", {
  enumerable: false,
  configurable: false,
  writable: false,
  value: function(){ console.log(this); }
});


g.push(5); 
g.push(9); 

var t;
for (t in g){
    console.log(g[t]);
}

prints 5 and 9 but does not list the "log" function

g.log() -> echos [5,9]

The key to this working is being able to flag a property as "enumerable: false" with marks the property as something that shouldn't be iterated over.

More on Object.defineProperty here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

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.