6

I'm using a javascript library which returns arrays not starting from zero like starting from 26 or 1500, what i want to do is a method to get the first element in that array regardless of the index number starting with 0 or any other number.

Are they any method to do this in javascript ?

azelix
  • 1,257
  • 4
  • 26
  • 50

6 Answers6

6

I suggest to use Array#some. You get the first nonsparse element and the index. The iteration stops immediately if you return true in the callback:

var a = [, , 22, 33],
    value,
    index;

a.some(function (v, i) {
    value = v;
    index = i;
    return true;
});

console.log(index, value);
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
2

The information below is generally useful, but for the problem the OP listed, Nina's answer is by far a better solution.


Those are called sparse arrays and they're one of the few situations where you may want to use for-in on an array.

Remember that arrays are objects in JavaScript, and array entries are properties keyed by names (array indexes) that meet certain criteria. So we can use the features that let us discover the properties on an object to find the indexes on your sparse array.

for-in example:

for (var n in theArray) {
    if (theArray.hasOwnProperty(n) && isArrayIndex(n)) {
        // Use theArray[n]
    }
}

This answer shows how you can determine that n is an array index as opposed to being some other property. A very technical definition would be

function isArrayIndex(n) {
    return /^0$|^[1-9]\d*$/.test(n) &&
           n <= 4294967294;
}

...but a definition that's good enough for most of us would be

function isArrayIndex(n) {
    return !isNaN(parseInt(n, 10));
}

Similarly, you can use Object.keys; since it only looks at own enumerable properties, you don't need the hasOwnProperty check:

Object.keys(theArray).forEach(function(n) {
    if (isArrayIndex(n)) {
        // ...
    }
});

Note that officially, neither of those is in any particular order, not even in ES2015 ("ES6"). So in theory, you could see the indexes out of numeric order. In the real world, I've never seen an even vaguely-modern JavaScript engine that returned array indexes out of order. They're not required to, but every one I've tried does.

So officially, you would need to get a full list and then find the minimum value in it:

var min = Object.keys(theArray).reduce(function(min, n) {
    var i = parseInt(n, 10);
    return isNaN(i) || (min !== undefined && min > i) ? min : i;
}, undefined);

That'll given you undefined if the array is empty, or the min index if it isn't. But if you want to make the assumption you'll get the keys in numeric order:

// Makes an assumption that may not be true
var min = +Object.keys(theArray).filter(isArrayIndex)[0];

If you're using a JavaScript engine that's entirely up-to-date, you can rely on the order returned by Object.getOwnPropertyNames, which is required to list the array indexes in order.

var min = +Object.getOwnPropertyNames(theArray).filter(isArrayIndex)[0];
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • @azelix: You're very welcome, but take a look at [Nina's answer](http://stackoverflow.com/a/37991354/157247) -- she has a **much** better way to get the first index. – T.J. Crowder Jun 23 '16 at 13:27
1

It may be useful to use a filter function on the array to get back a normalised array.

var fullArray = array.filter(function(n){
    return n != undefined;
});
fullArray[0]

The answers here may help you decide Remove empty elements from an array in Javascript

Community
  • 1
  • 1
Ben F Lodge
  • 21
  • 1
  • 4
  • 1
    you lose the index, if that is important. – Nina Scholz Jun 23 '16 at 12:02
  • Actually, that filters out two completely different things: *Missing* entries (because `filter` doesn't visit those at all), and entries that are present but have the value `undefined`. If you just want to filter out *missing* entries, `var fullArray = array.filter(function() { return true; });` – T.J. Crowder Jun 23 '16 at 12:05
1

I guess one alternative to Array.prototype.some() is the Array.prototype.findIndex() method. These are much faster than filter alone and will keep your array and indices untouched.

var arr = new Array(1000),
     fi = -1;
arr[777] = 1453; // now we have a nice sparse array
fi = arr.findIndex(f => f !== void 0); // void 0 is the perfect undefined
console.log(fi);
console.log(arr[fi]);
Redu
  • 25,060
  • 6
  • 56
  • 76
  • 1
    This requires a **lot** more calls to the callback than Nina's `Array#some` (because `findIndex` visits sparse elements), and your condition filters out entries that are present but have the value `undefined`, not just missing entries. – T.J. Crowder Jun 23 '16 at 13:33
  • @T.J. Crowder : "because findIndex visits sparse elements" No it does not and it would be very pointless if it did. Please read first paragraph. https://developer.mozilla.org/tr/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex#Description – Redu Jun 23 '16 at 13:38
  • Do you *really* think I'd say that if I weren't sure? If I hadn't checked? I checked [the spec](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.findindex) and proved it experimentally before commenting. https://jsfiddle.net/hwk7fuah/ I'm sorry MDN mislead you, I hate when that happens, but you might have taken the time to give it a quick test before being snarky. :-) MDN is extremely good most of the time, but it *is* community-edited and sometimes clangers like that one make their way in. I've fixed it. – T.J. Crowder Jun 23 '16 at 13:49
  • @T.J. Crowder : OK sorry for sounding snarly. Apologies. But there is this; `a = new Array(1000)` doesn't produce any indices and `a[777] = 9` produces only "1" index key (property of array object) as 777. Check with a for in loop you will get only one property. So no array method including `findIndex` can not visit not existing properties even if they wanted to. We need fill() first. But most importantly findIndex & indexOf are two of the best optimized tools in JS. Please check a comparison of findIndex and some on 1M items sparse array in this fiddle https://jsfiddle.net/hwk7fuah/2/ – Redu Jun 23 '16 at 14:03
  • *"Check with a for in loop you will get only one property."* Yes, I know how sparse arrays work. *"So no array method including findIndex can not visit not existing properties even if they wanted to."* Sure they can, and many do: Look at `Array#some`, it doesn't visit nonexistant entries. Neither does `forEach`. Or `filter`. Or `map`. `find` and `findIndex` are exceptions to the general rule. – T.J. Crowder Jun 23 '16 at 14:15
  • @T.J. Crowder: As per the `undefined` comment. I am using `void 0` for some reason and it is `void 0 === "undefined" // false`so if OP had "undefined" in his array as a first non sparse element he will still get it. I doubt OP will be interested in this because he phrases his question such that we understand he is not interested in the undefined items while with a slight possibility he might with "undefined" items. This was my understanding. – Redu Jun 23 '16 at 14:17
  • please try `ar[666] = undefined;`. – Nina Scholz Jun 23 '16 at 16:28
  • @Nina Scholz: Sorry i was out drinking lots of beer... just got back home an tested `arr[666] = undefined` or `var a; arr[666] = a;` and it won't find them as expected. Just couldn't get the point... – Redu Jun 23 '16 at 19:43
  • the point is, if you take your assingment of `arr[777] = 1453;` and my wanted `ar[666] = undefined;` and perform your find index, you won't get 666/undefined, but 777/1453. but 666/undefined is the first nonsparse item. – Nina Scholz Jun 23 '16 at 19:48
  • @Nina Scholz yes true. I am not just passing the sparse slots but i am passing the ones which are `undefined` (including the sparse ones "except" the ones which are `"undefined"`). Why in the world the OP would like to have an `undefined` item as a first entry... That's my understanding from the question. If he does... well then i will do my check with the `in` operator and the world peace would be set again. – Redu Jun 23 '16 at 19:56
  • *and the world peace would be set again* calm down. – Nina Scholz Jun 23 '16 at 19:58
  • @Nina Scholz just joking...i am very happy tonight. :) – Redu Jun 23 '16 at 19:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/115446/discussion-between-nina-scholz-and-redu). – Nina Scholz Jun 23 '16 at 20:00
1
const arr = [0,1,2]

// using destructuring to get the first element

let [first] = arr

// plus: using destructuring to get the last element

let [first] = [...arr].reverse()
0

With this piece of code you can find first assigned value index and then get the value from your array:

var a = [, , 22, 33];
var value = a.find((v, i) => i in a);
console.log(value);
/* ---------------------------------------------- */
var i = 0
while (!(i in a) && i < a.length) i++; // If i === a.length then the array is emtpy
console.info(i, a[i]);

First implementation uses Array.prototype.find which makes less variable usage so this is cleaner but to find the index you should call indexOf over the array.

But the second one is a little bit old fashioned but give the chance of having index without extra efforts.


BTW Nina's seems better. (can make it shorter?)

Community
  • 1
  • 1
Morteza Tourani
  • 3,506
  • 5
  • 41
  • 48