Related
Caution:
question still applies to for…of loops.> Don't use for…in to iterate over an Array, use it to iterate
over the properties of an object. That said, this
I understand that the basic for…in syntax in JavaScript looks like this:
for (var obj in myArray) {
// ...
}
But how do I get the loop counter/index?
I know I could probably do something like:
var i = 0;
for (var obj in myArray) {
alert(i)
i++
}
Or even the good old:
for (var i = 0; i < myArray.length; i++) {
var obj = myArray[i]
alert(i)
}
But I would rather use the simpler for-in loop. I think they look better and make more sense.
Is there a simpler or more elegant way?
In Python it's easy:
for i, obj in enumerate(myArray):
print i
for…in iterates over property names, not values, and does so in an unspecified order (yes, even after ES6). You shouldn’t use it to iterate over arrays. For them, there’s ES5’s forEach method that passes both the value and the index to the function you give it:
var myArray = [123, 15, 187, 32];
myArray.forEach(function (value, i) {
console.log('%d: %s', i, value);
});
// Outputs:
// 0: 123
// 1: 15
// 2: 187
// 3: 32
Or ES6’s Array.prototype.entries, which now has support across current browser versions:
for (const [i, value] of myArray.entries()) {
console.log('%d: %s', i, value);
}
For iterables in general (where you would use a for…of loop rather than a for…in), there’s nothing built-in, however:
function* enumerate(iterable) {
let i = 0;
for (const x of iterable) {
yield [i, x];
i++;
}
}
for (const [i, obj] of enumerate(myArray)) {
console.log(i, obj);
}
demo
If you actually did mean for…in – enumerating properties – you would need an additional counter. Object.keys(obj).forEach could work, but it only includes own properties; for…in includes enumerable properties anywhere on the prototype chain.
In ES6, it is good to use a for... of loop.
You can get index in for... of like this
for (let [index, val] of array.entries()) {
// your code goes here
}
Note that Array.entries() returns an iterator, which is what allows it to work in the for-of loop; don't confuse this with Object.entries(), which returns an array of key-value pairs.
How about this
let numbers = [1,2,3,4,5]
numbers.forEach((number, index) => console.log(`${index}:${number}`))
Where array.forEach this method has an index parameter which is the index of the current element being processed in the array.
Solution for small array collections:
for (var obj in arr) {
var i = Object.keys(arr).indexOf(obj);
}
arr - ARRAY,
obj - KEY of current element,
i - COUNTER/INDEX
Notice: Method keys() is not available for IE version <9, you should use Polyfill code.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
For-in-loops iterate over properties of an Object. Don't use them for Arrays, even if they sometimes work.
Object properties then have no index, they are all equal and not required to be run through in a determined order. If you want to count properties, you will have to set up the extra counter (as you did in your first example).
loop over an Array:
var a = [];
for (var i=0; i<a.length; i++) {
i // is the index
a[i] // is the item
}
loop over an Object:
var o = {};
for (var prop in o) {
prop // is the property name
o[prop] // is the property value - the item
}
As others have said, you shouldn't be using for..in to iterate over an array.
for ( var i = 0, len = myArray.length; i < len; i++ ) { ... }
If you want cleaner syntax, you could use forEach:
myArray.forEach( function ( val, i ) { ... } );
If you want to use this method, make sure that you include the ES5 shim to add support for older browsers.
Answer Given by rushUp Is correct but this will be more convenient
for (let [index, val] of array.entries() || []) {
// your code goes here
}
Here's a function eachWithIndex that works with anything iterable.
You could also write a similar function eachWithKey that works with objets using for...in.
// example generator (returns an iterator that can only be iterated once)
function* eachFromTo(start, end) { for (let i = start; i <= end; i++) yield i }
// convers an iterable to an array (potential infinite loop)
function eachToArray(iterable) {
const result = []
for (const val of iterable) result.push(val)
return result
}
// yields every value and index of an iterable (array, generator, ...)
function* eachWithIndex(iterable) {
const shared = new Array(2)
shared[1] = 0
for (shared[0] of iterable) {
yield shared
shared[1]++
}
}
console.log('iterate values and indexes from a generator')
for (const [val, i] of eachWithIndex(eachFromTo(10, 13))) console.log(val, i)
console.log('create an array')
const anArray = eachToArray(eachFromTo(10, 13))
console.log(anArray)
console.log('iterate values and indexes from an array')
for (const [val, i] of eachWithIndex(anArray)) console.log(val, i)
The good thing with generators is that they are lazy and can take another generator's result as an argument.
On top of the very good answers everyone posted I want to add that the most performant solution is the ES6 entries. It seems contraintuitive for many devs here, so I created this perf benchamrk.
It's ~6 times faster. Mainly because doesn't need to: a) access the array more than once and, b) cast the index.
That's my version of a composite iterator that yields an index and any passed generator function's value with an example of (slow) prime search:
const eachWithIndex = (iterable) => {
return {
*[Symbol.iterator]() {
let i = 0
for(let val of iteratable) {
i++
yield [i, val]
}
}
}
}
const isPrime = (n) => {
for (i = 2; i < Math.floor(Math.sqrt(n) + 1); i++) {
if (n % i == 0) {
return false
}
}
return true
}
let primes = {
*[Symbol.iterator]() {
let candidate = 2
while (true) {
if (isPrime(candidate)) yield candidate
candidate++
}
}
}
for (const [i, prime] of eachWithIndex(primes)) {
console.log(i, prime)
if (i === 100) break
}
To use for..of loop on array and retrieve index you can you use array1.indexOf(element) which will return the index value of an element in the loop. You can return both the index and the value using this method.
array1 = ['a', 'b', 'c']
for (element of array1) {
console.log(array1.indexOf(element), element) // 0 a 1 b 2 c
}
As mentionned in comments, this will return false index when the array contains non uniques values. (considering arr = ['a', 'b', 'c', 'a'], index of arr[3] will return 0 instead of 3)
// this loop is used in advanced javascript
//For Example I have an array:
let array = [1,2,3,4,5];
1) for(let key in array){
console.log(key);//this shows index of array {Result: 0,1,2,3,4}
console.log(array[key]);//this show values of array {Result: 1,2,3,4,5}
}
//Hopefully, You will quickly understand;
I have a nested array like this.
var arr = [[false,false,false,false],[false,false,false,false],[false,false,false,false],[false,false,false,false]]
I want to check if every value is false. I could think of one way of doing this.
let sum = 0;
arr.forEach((row, i) => {
row.forEach((col, j) => {
sum = sum +arr[i][j]
});
});
if(sum === 0){
console.log("all values false")
}
This works. But I'm curious if there is a better way? to check if all values are true or false?
You can use nested every()
var arr = [[false,false,false,false],[false,false,false,false],[false,false,false,false],[false,false,false,false]]
const res = arr.every(x => x.every(a => a === false));
console.log(res)
To make the code a little more cleaner you can first flat() and then use every()
var arr = [[false,false,false,false],[false,false,false,false],[false,false,false,false],[false,false,false,false]]
const res = arr.flat().every(x => x === false)
console.log(res)
I am considering you want to check of only false. If you want to check for all the falsy values(null, undefined etc). You can use ! instead of comparison with false
const res = arr.every(x => x.every(a => !a));
You could take two nested Array#some, because if you found one true value the iteration stops. Then take the negated value.
var array = [[false, false, false, false], [false, false, false, false], [false, false, false, false], [false, false, false, false]],
result = !array.some(a => a.some(Boolean));
console.log(result);
You can use .array.every() method:
var arr = [[false,false,false,false],[false,false,false,false],[false,false,false,false],[false,false,false,false]]
let result = arr.every(x => x.every(y => y === false));
console.log(result);
I have nested array like this,
var a=[1,2,[3,4,[5,6]]];
I am trying to convert it to flatten array.
var a=[1,2,[3,4,[5,6]]];
var b = a.join(',');
console.log(b);
var c = JSON.parse("[" + b + "]")
console.log(typeof c);
I used join and JSON.parse() methods, I am able to convert to a flat array, but when I do typeof for the array 'c', it says the type as 'object but not an array. My question is as everything is considered as an 'Object' in Javascript. Is returning the array 'c' as 'object' is correct or not?
Arrays are a type of object which is why it is printing object to the console. To test if an array is an array you need to use Array.isArray(someVar).
Note: You can use flat() to flatten the array.
let a = [1,2,[3,4,[5,6]]]
// 2 is the depth
console.log(a.flat(2))
console.log(typeof a)
// Prints true
console.log(Array.isArray(a))
// Prints false
console.log(Array.isArray({a:123}))
// Prints false
console.log(Array.isArray(123))
// Prints false
console.log(Array.isArray('Kitty'))
You can recursively flatten the array with reduce like this. If the item inside the array is an array, flatten the array with the same function.
const flatten = (arr) => {
return arr.reduce((acc,item) => {
if (Array.isArray(item)) {
return acc.concat(flatten(item))
}
return acc.concat(item)
}, [])
}
const a = [1,2,[3,4,[5,6]]];
console.log(flatten(a))
Try:
obj instanceof Array
Also something to consider -- if you're passing the Array across frame boundaries, you will need to do the following:
Object.prototype.toString.call(obj) === '[object Array]'
See related post: How to detect if a variable is an array
The best method you can use to check if c is an array is Array.isArray(), and it gives true for c so you are "safe":
var a=[1,2,[3,4,[5,6]]];
var b = a.join(',');
var c = JSON.parse("[" + b + "]");
console.log(c);
console.log("c is Array?: " + Array.isArray(c));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
And yes, it is ok the typeof for an array gives object because from MDN:
Arrays are list-like objects whose prototype has methods to perform traversal and mutation operations...
var a=[1,2,[3,4,[5,6]]];
console.log(typeof a);
var b = new Array(10);
console.log(typeof b);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
Now, if you need to flatten your array, you can use the experimental Array.flat() it takes and optional deep argument (default to 1). However note this method can not be available on some browser/version. In this, can you can use the next version with recursion:
const flattenDeep = (arr) =>
{
return arr.reduce(
(acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val),
[]
);
}
console.log(flattenDeep([1,2,[3,4,[5,6]]]));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
I quite simple one:
I have a Javascript object with some properties whose values are arrays, with the following structure:
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"],...}
I need to get an array of arrays with only the values, like the following:
let obj2 = [["xxx#yyy.com"], ["qqq#www.com"], ["asdf"],...]
With Object.values(obj), I get [["xxx#yyy.com", "qqq#www.com"], ["asdf"],...], which is not exactly what I am looking for, but it is a good starting point...
Also, I am looking for a one-liner to do it, if possible. Any ideas?
Thanks.
An alternative using the function reduce.
This approach adds objects and arrays from the first level.
As you can see, this approach evaluates the type of the object.
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"]}
var result = Object.values(obj).reduce((a, c) => {
if (Array.isArray(c)) return a.concat(Array.from(c, (r) => [r]));
return a.concat([c]);
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One line approach (excluding the checking for array type):
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"]},
result = Object.values(obj).reduce((a, c) => (a.concat(Array.from(c, (r) => [r]))), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Object.values to get array of values and then concat and spread syntax ... to get flat array and then map method.
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"]}
const values = [].concat(...Object.values(obj)).map(e => [e])
console.log(values)
How to transform {2:'b',3:'c',1:'a'} into [{1:'a'},{2:'b'},{3:'c'}] by lodash?
It's fairly trivial using Object.keys + Array.map, you really don't need lodash:
const obj = {2:'b',3:'c',1:'a'};
const arr = Object.keys(obj).map(key => ({ [key]: obj[key] }))
console.log(arr)
Regarding the lack of a sort function, the above code is exploiting the fact that numerically indexed Object keys are (per the spec) stored sequentially. Check the order for yourself:
console.log({2:'b',3:'c',1:'a'})
Here is the relevant portion of the spec
9.1.12 [[OwnPropertyKeys]] ( )
When the [[OwnPropertyKeys]] internal method of O is called the
following steps are taken:
Let keys be a new empty List.
For each own
property key P of O that is an integer index, in ascending numeric
index order
2a. Add P as the last element of keys.
With upcoming Javascript with Object.entries, you could map a new array with single objects.
var data = {2:'b',3:'c',1:'a'},
result = Object
.entries(data)
.sort((a, b) => a[0] - b[0])
.map(([k, v]) => ({ [k]: v }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
With lodash, you could use
_.chain,
_.toPairs,
_.sortBy,
_.map and
_.fromPairs
var data = {2:'b',3:'c',1:'a'},
result = _
.chain(data)
.toPairs(data)
.sortBy([0])
.map(o => _.fromPairs([o]));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Lodash is not really necessary to accomplish what you want, but I'm still adding it anyway and add a sorted function. I've also included the native JavaScript way.
const obj = {b: 3, c: 2, a: 1};
const sortByKeys = object => {
const keys = Object.keys(object)
const sortedKeys = _.sortBy(keys)
return _.map(sortedKeys, key => ({ [key]: object[key]}))
}
// the lodash way, and sorted
console.log(sortByKeys(obj))
// simpler way
const result = Object.keys(obj)
.map(key => ({ [key]: obj[key] }))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Why use lodash? Just use regular Javascript. Solution can be cleaned up a bit but the idea is to loop through your object and push your desired format into a new array. I also throw the sorting in there for convenience, but feel free to re-factor to your liking.
const obj = {2:'b',3:'c',1:'a'}
let newArr = [];
for (var key in obj) {
newArr.push({[key]: obj[key]})
newArr.sort((a, b) => a[key] > b[key])
}
console.log(newArr)