Is there any way to create a generic for loop that will loop through either an array or an object correctly? I know I can write the following for loop, but it will also loop through other properties that would be added to an array.
for (item in x) {
console.log(item)
}
By this I mean a for loop that will iterate:
x = [1, 2]
x.foo = "foo"
y = {first:1, second: 2}
x as
1
2
y as
first
second
The reason behind this is that I won't know until runtime what x will be (either an Array or an Object). Is my only option to create a function that will check at runtime?
Use the for..of loop.
Iterating over arrays
const array = [1, 2];
array.foo = "test";
for (const number of array) {
console.log(number); // skips array.foo
}
Iterating over objects
const object = {
some: "string",
number: 42
};
for (const [key, value] of Object.entries(object)) {
console.log(key, value);
}
Anyway, from a code-style point of view, you should still check whether your object is an array before you iterate over it. You can use Array.isArray to achieve that. So, assuming data is either an object or an array:
if (Array.isArray(data)) {
for (const element of data) {
// Iterate over array
}
}
else {
for (const [key, value] of Object.entries(data)) {
// Iterate over object
}
}
Generic looping
Since in JavaScript, typeof [] === "object" (i. e. arrays are objects that use the element's index as its key), you could reduce it to a single loop with Object.entries:
for (const [key, value] of Object.entries(data)) {
// For arrays, `key` will be the index
}
Beware though that this latter method will not do justice to your exclusion of dynamic properties (e. g. array.foo), as you'll iterate over the result of Object.entries. If you do need to make this exclusion, use two for..of loops with Array.isArray as shown above.
If it's just the index/key values you need as per your expected output, here's a simple one-liner.
function loop(x) {
return (Array.isArray(x) ? x : Object.keys(x)).forEach(el => console.log(el));
}
loop(x); // 1 2
loop(y); // First Second
DEMO
Related
For Example :
I have an object and in that object, I have values in the array. I want to return the array which contains the key which contains the value passing as a variable.
function getValues(val , object){
return []; // return [b,c] because xyz are present in both
}
var object = {
"a" : ["abc", "cde","efg"],
"b" : ["asdf","asee","xyz"],
"c" : ["asaw","wewe","xyz"]
getValues("xyz", object);
}```
There are various ways to approach this.
One could be using Object's native functions:
function getValues(str, obj) {
return Object
// returns the entries pairs in a array of [key, value]
.entries(obj)
// from the array it searches the value for the string inputted and maps it back
.map(([key, array]) => array.includes(str) ? key : undefined)
// simply remove the undefined returned values from map()
.filter((value) => value);
};
This could have been done with reduce() as well but I found it easier to explain this way.
Another approach is using a for loop to iterate over the keys of the object:
function getValues(str, obj) {
let arr = [];
// Iterates through the object selecting its keys
for (let key in obj) {
// If the array of the current key has the string in it, includes in the array
if (obj[key].includes(str)) {
arr.push(key)
}
}
return arr;
}
I'm trying to iterate through an object but getting an error
const myObj = {
number1: 4,
number2: 10
}
for (const key of myObj) {
console.log(`${key}: ${myObj[key]}`);
}
but instead getting a console error
You are actually making a very minor mistake. You are creating an object and using for.. of for that but for..of is use to iterate over an array. Use for..in instead which iterates over an object keys.
Here's your working demo:
const myObj = {
number1: 4,
number2: 10
}
for (const key in myObj) {
console.log(`${key}: ${myObj[key]}`);
}
Use for..in instead of for..of.
The for...in statement iterates over all enumerable
properties of an object that are keyed by strings (ignoring ones keyed by Symbols),
including inherited enumerable properties.
The for...of statement creates a loop iterating over iterable
objects, including: built-in String, Array, array-like
objects (e.g., arguments
or NodeList), TypedArray, Map,
Set, and user-defined iterables. It invokes a custom iteration hook with
statements to be executed for the value of each distinct property of the object.
Try below.
const myObj = {
number1: 4,
number2: 10
}
for (const key in myObj) {
console.log(`${key}: ${myObj[key]}`);
}
Your Error is using for ... of instead of for... in in your loop. To iterate through object I think that using Object methods is better to process your data:
Object.entries(myObj2).forEach(([key, value]) => console.log(key, value))
You can also use keys method that gives you only the key and values that gives you only the values.
Here's good variant, this works perfect:
const myObj = {
number1: 4,
number2: 10,
};
Object.keys(myObj).forEach((key) => {
console.log(`${key}: ${myObj[key]}`);
});
Replace of with in
for...of is used in String, Array, array-like objects.
Whereas in is an operator that returns true if the specified property is in the specified object.
const myObj = {
number1: 4,
number2: 10
}
for (const key in myObj) {
console.log(`${key}: ${myObj[key]}`);
}
Using Slickgrid I am trying to set the CSS of a cell using setCellCssStyles method
for (var rowIndx = 1; rowIndx < movmentRows.length; rowIndx++) {
grid.setCellCssStyles("disabled", {
rowIndx: {
san: "slick-cellreadonly",
ean: "slick-cellreadonly",
},
});
}
I understand its because I am using a variable for a key in the for loop.
But I don't understand how to make this work.
I tried to replace rowIndx with [rowIndx] but I am getting syntax error, so I think my JavaScript is not ES6. Then I tried the following but this is also giving syntax error with -
Encountered '['and Expected keyword or brace.
for(var rowIndx=1;rowIndx<movmentRows.length;rowIndx++){
var key = {};
grid.setCellCssStyles("natCol-greyed", {
key[rowIndx] : {
sourceAccountNational: "slick-cellreadonly",
excludeAccountNational: "slick-cellreadonly"
}
});
}
Please suggest.
If you are trying to create an object but want to use a variable for the key, the syntax is as follows:
let keyName = "foo"
let object = { [keyName]: "bar" }
// gives the object `{"foo": "bar}
in older javascript you have to assign to a key after the object is created:
let keyName = "foo"
let object = {}
object[keyName] = "bar"
Also, I try to never do a "manual" loop when I can use an iterator. It's less error prone than a "manual" loop, and allows you to use different types of collections other than just arrays, so it's more flexible. there are two main ways to do this:
A for … of loop:
let collection = ["a", "b", "c"]
for (let value of collection) {
console.log("value:", value)
}
// if you also want the index
for (let [index, value] of collection.entries()) {
console.log("value and index:", value, index)
}
// or _just_ the indexes (note this skip empty values in sparse arrays)
for (let index of collection.keys()) {
console.log("index:", index)
}
Using forEach:
collection.forEach((item, index) => console.log(item, index))
Both these methods usually work about the same, but the for … of loop is closer to a "manual" loop. However, forEach is a bit older, so may be available more places. Also, take a look at the similar map.
If movementRows is an array you can use a for...of loop like so:
for (const [index, row] of movmentRows.entries()) { //index is the index of each itteration and row is the value
grid.setCellCssStyles("disabled", {
[row]: {
san: "slick-cellreadonly",
ean: "slick-cellreadonly",
},
});
}
To set an object key as a variable you can put it in brackets..
Note on for..of loop: It is not supported in IE.
More on how to dynamically set object properties
More on For...Of loop
I'm building my own map method to be as close as the native map method.
Since the native map pushes(i think) the changed values into a new array, it still keeps the empty slots. I wasn't able to find a solution to push an empty slot into an array, like this example below.
[1, 2, 3].push(some code) // [1, 2, 3, empty]
I tried pushing an array with one empty item prefixed with a spread operator arr.push(...(new Array(1))) or arr.push(...[,]) but that just pushes undefined.
I solved my problem by not using push and instead assigning values to the array index that way skipped indices will be set to empty.
But I'm writing this post to see if anyone knows that if it's possible to use the push method to push an empty slot to an array.
No, it's not possible, not with the push method. empty can only exist if the array has a certain length, but a whole number property of the array does not exist at some index. This is called a sparse array, and cannot be created with push (or other array methods, if they're called on and with non-sparse arrays).
The only way to do so would be to assign to an index for which a lower index doesn't exist yet.
Look at the results for the below two snippets in your browser console, not the snippet console:
const arr = [];
arr[1] = 'a';
console.log(arr);
Or to set the .length of the array above the last index that the array has:
const arr = [];
arr.length = 1;
console.log(arr);
But the two approaches above are very weird to do and probably have no good reason to be used. Better to avoid sparse arrays entirely.
Keep in mind that an empty slot is different from undefined, which is perfectly possible to have as an array value:
const arr = [];
arr.push(undefined);
console.log(arr);
You can create an empty slot in an array by incrementing the array length:
var a = []
a.push(1)
a.length++
a.push(3)
console.log(a)
console.log(1 in a) // anything at index 1?
Alternatively, you can push something and then delete it:
var a = []
a.push(1)
a.push(2)
a.push(3)
delete a[1]
console.log(a)
console.log(1 in a) // anything at index 1?
There is no need to actually push to a new array in your implementation. You can simply do new Array(this.length) where this.length is the array you are mapping through length.
For example consider this map implementation:
if (!Array.prototype.mapIt) {
Object.defineProperty(Array.prototype, "mapIt", {
value: function(fn) {
if (this === null) {
throw new TypeError('Array.prototype.mapIt called on null or undefined');
}
if (typeof fn !== 'function') {
throw new TypeError('predicate must be a function');
}
let _array = this.filter(x => x != null) // remove empty values
let result = new Array(_array.length) // the new array we will return
for (var i = 0; i < _array.length; i++) {
result[i] = fn.call(arguments[1], _array[i], i, _array) // call the predicate
}
return result;
}
});
}
let arr = [1, 2, , , 3] // the test array
let result = arr.mapIt((c, i, a) =>
console.log(`current: ${c}`, `index: ${i}`, `array: ${a}`) || c + 2)
console.log('result: ', result)
console.log('original array: ', arr)
Hope this helps you with an gives you an idea about a possible map implementation.
I have this piece of code,
let array = ["cj1rdd9fc00013f69ccln57g0", "cj1rdda8x00023f69g9281ay8"];
for (let [key, value] of array) {
console.log(key, value);
}
I expect, 0 cj1rdd9fc00013f69ccln57g0 and 1 cj1rdda8x00023f69g9281ay8 as the output. what am I missing here? How can I get the desired output?
Here is a bit of under the hood explanation.
for..of loop works on iterables. On each iteration it calls iterator.next().value to get values. Standard Array implementation has two kinds of iterators - the one that returns only values and the other that returns [key, value] pairs. If you need to get the second type of iterator, use array.entries().
let array = ["cj1rdd9fc00013f69ccln57g0", "cj1rdda8x00023f69g9281ay8"];
for (let [key, value] of array.entries()) {
console.log(key, value);
}
Here is the demo of two types of iterators:
var arr = ['a','b'];
var valueIterator = arr[Symbol.iterator]();
valueIterator.next().value; // returns a
valueIterator.next().value; // returns b
var arr = ['a','b'];
var valueKeyIterator = arr.entries();
valueKeyIterator.next().value; // returns [0, a]
valueKeyIterator.next().value; // returns [1, b]
Let try to understand whats happening here:
As per MDN-Docs, for..of provides value of each iteration to the assignment.
So when you do for(var v of array), v will hold value.
Now when you do let [key, value], you are using something thats called as Destructuring Assignment.
What it does is, for given list of variables, it will assign values at corresponding index value.
So, in let [a,b] = [10, 20], a=10 and b=20.
Coming back to your example,
let [key, value] of array,
is evaluated as let [key, value] = "cj1rdd9fc00013f69ccln57g0", so key holds value at 0th index, a.k.a c and value holds j.
How can I get the desired output
You can use other looping mechanisms like for, array.forEach etc.
There are other mechanisms like .map, .filter, .some, .every, .reduce but they have their own usecase, and can be suggested based on the processing logic.
Array.forEach
let array = ["cj1rdd9fc00013f69ccln57g0", "cj1rdda8x00023f69g9281ay8"];
array.forEach(function(value, index){
console.log(index, value)
})
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of
The way for of works is each time it will grab just the value, not the index.
let array = ["cj1rdd9fc00013f69ccln57g0", "cj1rdda8x00023f69g9281ay8"];
for (let item of array) {
console.log(item);
}
If you want to include the index, see this question:
How do you get the loop counter/index using a for-in syntax in JavaScript?
This is a destructuring assignment . This will work for es6 Maps and not for Arrays . For arrays you can just use a for of loop .
This Code, Console.log Array
let array = ["cj1rdd9fc00013f69ccln57g0", "cj1rdda8x00023f69g9281ay8"];
var i,j,tempstring;
for (i=0,j=array.length; i<j; i++) {
tempstring = array[i];
console.log(tempstring);
}
If you want to get the value and index of an array in every iteration using some fancy iteration, try this.
let array = ["cj1rdd9fc00013f69ccln57g0", "cj1rdda8x00023f69g9281ay8"];
array.map((value, index) => {
console.log(index, key);
});