How to iterate over Object's property-value pairs? - javascript

I have a structure like this:
var myMap = {
partnr1: ['modelA', 'modelB', 'modelC'],
partnr2: ['modelA', 'modelB', 'modelC']
};
I am going to iterate through each of the elements (partnr) with their associatives (models).
I am trying a double $each() iteration in order to achieve this, but nothing happens:
$.each(myMap, function (i, val) {
$.each(i, function (innerKey, innerValue) {
setTimeout(function () {
$('#variant').fadeOut("slow", function () {
$(this).text(innerKey + "-" + innerValue).fadeIn("slow");
});
}, i * 6000);
});
});
The effect with fading in and out that I am trying to achieve is working fine when using a single value array (Object), but not when I need to have more than one value for each key like here.
How to accomplish this iteration successfully? Are there other ways than using an Object that would be better in this case?

An answer to your Question from 2019:
It depends on what version of ECMAScript you use.
Pre ES6:
Use any of the answers below, e.g.:
for (var m in myMap){
for (var i=0;i<myMap[m].length;i++){
... do something with myMap[m][i] ...
}
}
For ES6 (ES 2015):
You should use a Map object, which has the entries() function:
var myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
for (const [key, value] of myMap.entries()) {
console.log(key, value);
}
For ES8 (ES 2017):
Object.entries() was introduced:
const object = {'a': 1, 'b': 2, 'c' : 3};
for (const [key, value] of Object.entries(object)) {
console.log(key, value);
}

I'd use standard javascript:
for (var m in myMap){
for (var i=0;i<myMap[m].length;i++){
... do something with myMap[m][i] ...
}
}
Note the different ways of treating objects and arrays.

Functional Approach for ES6+
If you want to take a more functional approach to iterating over the Map object, you can do something like this
const myMap = new Map()
myMap.forEach((value, key) => {
console.log(value, key)
})

Well, it looks like this old JQuery thread has been coopted by ES6 Map users.
If this is what you're looking for, may I suggest using the Array.from() function which converts the Map to an Array. This allows you to easily chain transforms such as filter(), map(), etc.
const map = new Map([
['key one', 'value one'],
['key two', 'value two'],
]);
// Loop through key-value-pairs
Array.from(map.entries()).map(([key, val]) => console.log(key, val));
// Loop through map keys
Array.from(map.keys()).map(key => console.log(key));
// Loop through values
Array.from(map.values()).map(value => console.log(value));

The callback to $.each() is passed the property name and the value, in that order. You're therefore trying to iterate over the property names in the inner call to $.each(). I think you want:
$.each(myMap, function (i, val) {
$.each(val, function(innerKey, innerValue) {
// ...
});
});
In the inner loop, given an object like your map, the values are arrays. That's OK, but note that the "innerKey" values will all be numbers.
edit — Now once that's straightened out, here's the next problem:
setTimeout(function () {
// ...
}, i * 6000);
The first time through that loop, "i" will be the string "partnr1". Thus, that multiplication attempt will result in a NaN. You can keep an external counter to keep track of the property count of the outer map:
var pcount = 1;
$.each(myMap, function(i, val) {
$.each(val, function(innerKey, innerValue) {
setTimeout(function() {
// ...
}, pcount++ * 6000);
});
});

Don't use iterators to do this. Maintain your own loop by incrementing a counter in the callback, and recursively calling the operation on the next item.
$.each(myMap, function(_, arr) {
processArray(arr, 0);
});
function processArray(arr, i) {
if (i >= arr.length) return;
setTimeout(function () {
$('#variant').fadeOut("slow", function () {
$(this).text(i + "-" + arr[i]).fadeIn("slow");
// Handle next iteration
processArray(arr, ++i);
});
}, 6000);
}
Though there's a logic error in your code. You're setting the same container to more than one different value at (roughly) the same time. Perhaps you mean for each one to update its own container.

We can use forEach Method available on maps From ES6 Version.
var myMap =new Map([
["partnr1", ['modelA', 'modelB', 'modelC']],
["partnr2", ['modelA', 'modelB', 'modelC']]
]);
myMap.forEach(function(values,key){
console.log(key);
/*****Do something with the models***********/
for(const [index,value] of values.entries()){
console.log(` ${key}[${index}] : ${value}`);
}
});

This is easily achieved using a javascript Map object. You simply iterate over the Map, using the fact that the map you're iterating over is included as an argument in each iteration call. Notice the map argument in the forEach function. This is the same Map object you're iterating over.
// Define the map
const myMap = new Map([
["key1", "value 1"],
["key2": "value 2"],
["key3": "value 3"]
])
// Iterate over the map, updating each value
myMap.forEach((value,key,map) => {
map.set(key, value + "A")
})
/*
Result: myMap now looks like this:
[
["key1", "value 1A"],
["key2": "value 2A"],
["key3": "value 3A"]
]
/*

Related

How to traverse through keys and values simultaneously in javascript (react native) [duplicate]

This question already has answers here:
How do I loop through or enumerate a JavaScript object?
(48 answers)
Closed 12 months ago.
I have a dictionary that has the format of
dictionary = {0: {object}, 1:{object}, 2:{object}}
How can I iterate through this dictionary by doing something like
for ((key, value) in dictionary) {
//Do stuff where key would be 0 and value would be the object
}
tl;dr
In ECMAScript 2017, just call Object.entries(yourObj).
In ECMAScript 2015, it is possible with Maps.
In ECMAScript 5, it is not possible.
ECMAScript 2017
ECMAScript 2017 introduced a new Object.entries function. You can use this to iterate the object as you wanted.
'use strict';
const object = {'a': 1, 'b': 2, 'c' : 3};
for (const [key, value] of Object.entries(object)) {
console.log(key, value);
}
Output
a 1
b 2
c 3
ECMAScript 2015
In ECMAScript 2015, there is not Object.entries but you can use Map objects instead and iterate over them with Map.prototype.entries. Quoting the example from that page,
var myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
var mapIter = myMap.entries();
console.log(mapIter.next().value); // ["0", "foo"]
console.log(mapIter.next().value); // [1, "bar"]
console.log(mapIter.next().value); // [Object, "baz"]
Or iterate with for..of, like this
'use strict';
var myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
for (const entry of myMap.entries()) {
console.log(entry);
}
Output
[ '0', 'foo' ]
[ 1, 'bar' ]
[ {}, 'baz' ]
Or
for (const [key, value] of myMap.entries()) {
console.log(key, value);
}
Output
0 foo
1 bar
{} baz
ECMAScript 5:
No, it's not possible with objects.
You should either iterate with for..in, or Object.keys, like this
for (var key in dictionary) {
// check if the property/key is defined in the object itself, not in parent
if (dictionary.hasOwnProperty(key)) {
console.log(key, dictionary[key]);
}
}
Note: The if condition above is necessary only if you want to iterate over the properties which are the dictionary object's very own. Because for..in will iterate through all the inherited enumerable properties.
Or
Object.keys(dictionary).forEach(function(key) {
console.log(key, dictionary[key]);
});
Try this:
dict = {0:{1:'a'}, 1:{2:'b'}, 2:{3:'c'}}
for (var key in dict){
console.log( key, dict[key] );
}
0 Object { 1="a"}
1 Object { 2="b"}
2 Object { 3="c"}
WELCOME TO 2020 *Drools in ES6*
Theres some pretty old answers in here - take advantage of destructuring. In my opinion this is without a doubt the nicest (very readable) way to iterate an object.
const myObject = {
nick: 'cage',
phil: 'murray',
};
Object.entries(myObject).forEach(([k,v]) => {
console.log("The key: ", k)
console.log("The value: ", v)
})
Edit:
As mentioned by Lazerbeak, map allows you to cycle an object and use the key and value to make an array.
const myObject = {
nick: 'cage',
phil: 'murray',
};
const myArray = Object.entries(myObject).map(([k, v]) => {
return `The key '${k}' has a value of '${v}'`;
});
console.log(myArray);
Edit 2:
To explain what is happening in the line of code:
Object.entries(myObject).forEach(([k,v]) => {}
Object.entries() converts our object to an array of arrays:
[["nick", "cage"], ["phil", "murray"]]
Then we use forEach on the outer array:
1st loop: ["nick", "cage"]
2nd loop: ["phil", "murray"]
Then we "destructure" the value (which we know will always be an array) with ([k,v]) so k becomes the first name and v becomes the last name.
The Object.entries() method has been specified in ES2017 (and is supported in all modern browsers):
for (const [ key, value ] of Object.entries(dictionary)) {
// do something with `key` and `value`
}
Explanation:
Object.entries() takes an object like { a: 1, b: 2, c: 3 } and turns it into an array of key-value pairs: [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ].
With for ... of we can loop over the entries of the so created array.
Since we are guaranteed that each of the so iterated array items is itself a two-entry array, we can use destructuring to directly assign variables key and value to its first and second item.
Try this:
var value;
for (var key in dictionary) {
value = dictionary[key];
// your code here...
}
You can do something like this :
dictionary = {'ab': {object}, 'cd':{object}, 'ef':{object}}
var keys = Object.keys(dictionary);
for(var i = 0; i < keys.length;i++){
//keys[i] for key
//dictionary[keys[i]] for the value
}
I think the fast and easy way is
Object.entries(event).forEach(k => {
console.log("properties ... ", k[0], k[1]); });
just check the documentation
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
using swagger-ui.js
you can do this -
_.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
console.log(n, key);
});
You can use below script.
var obj={1:"a",2:"b",c:"3"};
for (var x=Object.keys(obj),i=0;i<x.length,key=x[i],value=obj[key];i++){
console.log(key,value);
}
outputs
1 a
2 b
c 3
As an improvement to the accepted answer, in order to reduce nesting, you could do this instead, provided that the key is not inherited:
for (var key in dictionary) {
if (!dictionary.hasOwnProperty(key)) {
continue;
}
console.log(key, dictionary[key]);
}
Edit: info about Object.hasOwnProperty here
You can use JavaScript forEach Loop:
myMap.forEach((value, key) => {
console.log('value: ', value);
console.log('key: ', key);
});

How to add an array of key value pairs to a Map in JavaScript?

I have an array of key value pairs where each key has an another array of constant length (i.e., 2) as the value. How can I add the entire array to a Map() without just doing Map.set(key, value) for every pair?
I know that while creating a Map() instance I could pass an iterable like an array to it ex: let x = new Map(arr); But this is not supported in IE 11 as per the documentation here. So, could anyone help me out with an alternate implementation.
At the end of the day, I just want to be able to access the two values with a string key. If there is an another way I could implement, please guide me.
Here is the example:
I created a key value mapping array as follows:
let arr = [
['ar', ['cl', 'bl']],
['bs', ['kl', 'ml']],
['cs', ['rk', 'uk']],
['da', ['mk', 'ak']]
];
let map = new Map(arr); // This isn't working.
Thank you.
If you need to support browsers where the Map is not implemented, just use ordinary object, where the 1st array item will be keys & 2nd (the sub-array) values:
var arr = [
['ar', ['cl', 'bl']],
['bs', ['kl', 'ml']],
['cs', ['rk', 'uk']],
['da', ['mk', 'ak']]
];
var map = arr.reduce(function (obj, item) {
obj[item[0]] = item[1];
return obj;
}, {});
console.log(map['ar'], map.bs);
Or ES2015 approach (won't work in IE11):
const map = arr.reduce((obj, item) => ({
...obj,
[item[0]]: item[1],
}), {});
If you want to access the subarray by the string key, you'd have to convert it from array anyways.
If you'd want to use Map, then (apart from fixing typos in your array definition) you'll extract the contents by Map.prototype.get function:
var map = new Map(arr);
console.log(map.get('bs'));
If you don't want to create a new object, another approach could be using Array.filter:
arr.filter(function (item) { return item[0] === 'ar' })[0][1];
arr.filter((item) => item[0] === 'ar')[0][1]; // ES2015
(potentially wrapped in a function)
You can use a object
let obj =
{ ar:['cl', 'bl'],
bs:['kl', 'ml'],
cs:['rk', 'uk'],
da:['mk', 'ak']
};
console.log(obj['bs'][1]); // displays 'ml'

for let of loop doesn't work?

When I use for in loop, it works, and for of loop just doesn't get anything :(
Here is my code
'use strict'
var match_table = [
{'project': 'Laveral', 'template': 'Blade'},
{'project': 'Ember.js', 'template': 'Handlebars'},
{'project': 'Meteor', 'template': 'Handlebars'},
];
// count project number by template
var templateMap = new Array();
match_table.forEach(function(listItem){
var template = listItem['template'];
if (!templateMap[template]) {
templateMap[template] = new Object();
}
templateMap[template]['name'] = template;
if (templateMap[template]['count']) {
templateMap[template]['count']++;
} else {
templateMap[template]['count'] = 1;
}
});
//console.log(templateMap);
// for loop fails
for (let value of templateMap) {
console.log(value);
}
templateMap.forEach(function(item) {
console.log(item);
})
also forEach doesn't output anything either~?!
for-of cannot iterate through objects (since they are not iterable as per the standard).
So you either must use the good old for-in
OR
Use the non-standardised yet Object.entries():
for (const [key, value] of Object.entries(obj)) {
console.log(key, value);
}
templateMap in your case is an object, not an array, since you assign string keys into it (and JS arrays indexes are numeric within [0; 2^32-1) range).
Is template numeric? It looks like you're about to misuse an Array as an Object. Try templateMap.push(new Object()) to append to the array instead.

Don't include an object in map JavaScript

Using LoDash to map over an object:
_.map(items, (item) = > {
if (Array.isArray(item)) {
// don't include this in final object
}
return _.assign({
foo: "bar"
}, item);
});
Wondering what my best tactic is for not including an object in the returned, mapped, object if the current object is an array?
You may use reduce which works perfectly for your case:
var items = [{a: 1}, ["asd"], {b: 2}];
var result = _.reduce(items, (res, item) => {
if (!Array.isArray(item)) {
_.assign(res, item);
}
return res;
}, {foo: "bar"});
See jsbin.
Another (and IMO a cleaner) solution is to use chaining:
results = _(items).reject(_.isArray).map(function(item) {
....
}).value()
I would expect a map function to always output the same keys as the input. The values maybe modified, but the keys remain the same.
There are other functions you can use to change the set of items like filter or some kind of aggregate or reduce.
This... just assign an empty object if item is an array, otherwise assign item:
_.map(items, (item) = > {
return _.assign({
foo: "bar"
}, Array.isArray(item)?{}:item);
});
Here's a little fiddle without the new anonymous function syntax: https://jsfiddle.net/xurm6vrr/1/

How to implement Javascript ECMA 5's array.map() for sparse array?

It should be quite easy to implement array.map() that is defined in ECMA-262, which takes a function and this function will be called by 3 arguments: element value, index, the array.
But what about for sparse array? Obviously we don't want to iterate from index 0 to 100,000 if only index 0, 1, 2, and 100,000 has an element and otherwise is sparse from index 3 to 99,999. I can think of using arr.slice(0) or arr.concat() to clone the array, and then put in the replaced values, but what if we don't use slice or concat, is there another way to do it?
The solution I came up with using slice() is:
Array.prototype.collect = Array.prototype.collect || function(fn) {
var result = this.slice(0);
for (var i in this) {
if (this.hasOwnProperty(i))
result[i] = fn(this[i], i, this); // 3 arguments according to ECMA specs
}
return result;
};
(collect is used to try out the code, as that's another name for map in some language)
It should be easy, but there are a few peculiar points.
The callback function is allowed to modify the array in question. Any elements it adds or removes are not visited. So it seems we should use something like Object.keys to determine which elements to visit.
Also, the result is defined to be a new array "created as if by" the array constructor taking the length of the old array, so we might as well use that constructor to create it.
Here's an implementation taking these things into account, but probably missing some other subtleties:
function map(callbackfn, thisArg) {
var keys = Object.keys(this),
result = new Array(this.length);
keys.forEach(function(key) {
if (key >= 0 && this.hasOwnProperty(key)) {
result[key] = callbackfn.call(thisArg, this[key], key, this);
}
}, this);
return result;
}
I am assuming Object.keys returns the keys of the array in numerical order, which I think is implementation defined. If it doesn't, you could sort them.
You don't need to use this.slice(0). You can just make result an array and assign values to any index:
Array.prototype.collect = Array.prototype.collect || function(fn) {
var result = [];
for(var i in this) {
if (this.hasOwnProperty(i)) {
result[i] = fn(this[i]);
}
}
return result;
}

Categories