How to improve my iteration over an object of objects? - javascript

I'm trying to save every 'percentage' value from a .json into an array:
This code is working right now:
let percentage = []
for(var key in res['data']){
percentage.push(res['data'][key].percentage);
}
But I feel like I can improve it to only use one line. I tried mapping the values to a variable, as I did in the past like this:
let percentage = res['data'].map(res => res.percentage);
But instead I'm getting a 'TypeError: res.data.map is not a function' error. How can improve my code to map it in just one line like this?:

data is an object, not an array, so you can't call map on it. But, you could use Object.values instead:
const res = {
data: {
0: {
percentage: 8.1
},
1: {
percentage: 47
},
2: {
percentage: 87
}
}
};
const percentage = Object.values(res.data).map(({ percentage }) => percentage);
console.log(percentage);

map function you can use with Array. Since you input is an object, use Object.values.
Object.values(res.data).map(({ percentage }) => percentage);

One short and easy way is to use lodash library (use npm to get it)
Therein, you will have a _.map method which you can use straightaway, like this
_.map(res.data, (obj) => obj.percentage)
Another way which many people here are suggesting are and why you should use it is:
1- The first thing here is the use of for..in also iterates over the object's prototype chain properties as well.
Here on mozilla page :
The Object.values() method returns an array of a given object's own
enumerable property values, in the same order as that provided by a
for...in loop (the difference being that a for-in loop enumerates
properties in the prototype chain as well).
So, you should be using Object.values to get only its own enumerables.
2- Once you have the values as list, you can run a map function on it.
Here, in the arrow function passed to map, do object destructuring {propertyName} (ES6 specific) to extract the property to the propertyName variable.
So , the code will become :
Object.values(res.data)
.map( ({percentage}) => percentage)
I hope this clears it all.
EOD.

YOu can do Object.values and then use map on that
let resp = {
"data": {
"0": {
"percentage": 8.1
},
"1": {
"percentage": 9.1
},
"2": {
"percentage": 10.1
}
}
}
let perc = Object.values(resp.data).map(function(item) {
return item.percentage;
})
console.log(perc)

data is not an array and you can not implement map() on object other than array. You have to use Object.values() to implement map():
let percentage = Object.values(res.data).map(res => res.percentage);

res.data is an object, not an array, so no map method is available at this point.
Since your data object has numerical keys, you can easily transform it into an array with Object.assign, then map it. You can also shorten the mapping function a bit by using destructuring:
let percentage = Object.assign([], res.data).map(({percentage}) => percentage);

Related

How can I use spread syntax to copy object properties from multiple unknown objects in JavaScript?

I would like to use spread syntax to copy properties from several unknown objects into one object. Something like this:
var array = [{a:0}, {b:1}, {c:2}]; // This array could hold any number of objects
// This function should take the objects in the array and use spread syntax to create a new object.
function merge(arr) { /* Code I wish I knew */ } // returns an object
var combo = merge(array);
console.log(combo); // {a:0, b:1, c:2}
I am aware of Object.assign. My reason for asking this question is just to see if this sort of thing can be done with spread syntax. Additionally, I'm concerned with V8's Hidden Classes. From what I understand, modifying an object after it is instantiated negatively impacts the performance of the Hidden Class. I've read that Spread doesn't have this issue, possibly because it instantiates the object with all of the properties of the other objects.
You can use a combination of Array.reduce with Object.assign:
var array = [{a:0}, {b:1}, {c:2}];
function merge(arr) {
return array.reduce((x,y) => Object.assign(x, y))
//
// per the comments, if we don't want to mutate the original object we can do instead:
// return array.reduce((x,y) => Object.assign(x, y), {})
}
var combo = merge(array);
console.log(combo);
You can use Array#reduce to iterate the array and merge the children into one object:
var array = [{a:0}, {b:1}, {c:2}];
function merge(arr) {
return arr.reduce((acc, cur) => ({ ...acc, ...cur }));
}
var combo = merge(array);
console.log(combo);

Is it bad to have "number strings" as object keys?

I have two APIs to work with and they can't be changed. One of them returns type like this:
{
type: 25
}
and to other API I should send type like this:
{
type: 'Computers'
}
where 25 == 'Computers'. What I want to have is a map of numeric indices to the string value like this:
{
'1': 'Food',
'2': 'Something',
....
'25': 'Computers'
....
}
I am not sure why, but it doesn't feel right to have such map with numeric value to string, but maybe it is completely fine? I tried to Google the answer, but couldn't find anything specific. In one place it says that it is fine, in another some people say that it's better not to have numeric values as object keys. So, who is right and why? Could somebody help me with this question?
Thanks :)
There's nothing wrong with it, but I can understand how it might look a little hinky. One alternative is to have an array of objects each with their own id that you can then filter/find on:
const arr = [ { id: 1, label: 'Food' }, { id: 2, label: 'Something' }, { id: 25, label: 'Computers' } ];
const id = 25;
function getLabel(arr, id) {
return arr.find(obj => obj.id === id).label;
}
console.log(getLabel(arr, id));
You can use the Map object for this if using regular object feels "weird".
const map = new Map()
map.set(25, 'Computers');
map.set(1, 'Food');
// then later
const computers = map.get(25);
// or loop over the map with
map.forEach((id, category) => {
console.log(id, category);
});
Quick Update:
As mentioned by others, using objects with key=value pairs is OK.
In the end, everything in javascript is an object(including arrays)
Using key-value pairs or Map has 1 big advantage( in some cases it makes a huge difference ), and that is having an "indexed" data structure. You don't have to search the entire array to find what you are looking for.
const a = data[id];
is nearly instant, whereas if you search for an id in an array of objects...it all depends on your search algorithm and the size of the array.
Using an "indexed" object over an array gives much better performance if dealing with large arrays that are constantly being updated/searched by some render-loop function.
Map has the advantage of maintaining the insertion order of key-value pairs and it also only iterates over the properties that you have set. When looping over object properties, you have to check that the property belongs to that object and is not "inherited" through prototype chain( hasOwnProperty)
m = new Map()
m.set(5, 'five');
m.set(1, 'one');
m.set(2, 'two');
// some other function altered the same object
m.__proto__.test = "test";
m.forEach((id, category) => {
console.log(id, category);
});
/*
outputs:
five 5
one 1
two 2
*/
o = {};
o[5] = 'five';
o[1] = 'one';
o[2] = 'two';
// something else in the code used the same object and added a new property
// which you are not aware of.
o.__proto__.someUnexpectedFunction = () => {}
for (key in o) {
console.log(key, o[key]);
}
/*
Output:
1 one
2 two
5 five
someUnexpectedFunction () => {}
*/
Map and objects also have 1 very important advantage(sometimes disadvantage - depending on your needs ). Maps/objects/Sets guarantee that your indexed values are unique. This will automatically remove any duplicates from your result set.
With arrays you would need to check every time if an element is already in the array or not.

How to mutate original array in Javascript .map() function?

For eg:
var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":"1500" }];
And you want to change the value of "salary" of each person in an original array.
If you want to mutate the original array, you can use Array#forEach function.
const persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary": 1500 }];
persons.forEach(item => item.salary += 1000);
console.log(persons)
Array#map creates a new array of the created items and returns that. After you need to assign the returned result.
let persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary": 1500 }];
persons = persons.map(item => {
item.salary += 1000;
return item;
});
console.log(persons);
You can mutate the objects directly iterating with map. If I understood you correctly.
persons.map(i => { i.salary = i.salary * 1.25; return i; });
console.log(persons);
// [{ "name":"A", "salary": 1875 }, { "name":"B", "salary": 2343.75 }]
Though it's anti-pattern and you should avoid performing mutations of your original array in map().
Use forEach. If your array is full of objects, and you simply want to mutate those objects — like in the original question — then you can simply have your callback function mutate the item passed to it in its first argument.
If your array is full of primitives, which can only be changed by assigning something else to the array slots, thereby mutating the array — or if for some other reason you want to reassign things to the array slots — you can still use forEach. The second parameter and third parameters passed to the callback are the array index of the element currently being operated on, and a reference to the array itself. So within the callback function, you can assign to the relevant array slot.
you can use a simple for loop for that
var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":"1500" }];
for(let element of persons){
element.salary*=2;
}
console.log(persons);
.map() function takes third parameter in its callback thats the instance of original array.
You could do something like this also:
var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":1500 }];
persons.map(function(person, key, array) {
array[key].salary *= 2;
});
console.log(persons);
I read other answers, you can use any of them, but I see some problems there.
I will mention 2 methodologies I have used in many different languages, map and forEach. map is a functional way of traversing a collection and creating some new collection with new (different or same) elements, independent of languages. With map, it is expected to create a new collection that is created by some mapping from initial collection. On the other hand, forEach is a method that eases traversing a collection by not using usual for loop syntax for collections, and mutating (or changing) each item if desired.
If you use map on a collection that contains objects, and change those objects in the mapper function, you might face with unexpected behavior. Beacuse you are changing directly the object you are operating on, and do not mapping it to another object. This object might can be considered as a state and computers works based on the state transfers. If you want to change that object, i.e. some state, it is absolutely ok, but based on the description, you should not use map for such a case. Because you are not creating a new array with some new values, but instead, mutating provided elements. Use forEach for such a case.
I have added an example here. You can click the link and take a look at the console, and see my what I mean in a more clear way.
As far as I know, based on my experience, mutations in map method is considered as bad practice and discouraged.
These two are added for different purposes and it would be better to use them as expected.
For more, see Mozilla Web Docs page for Array.
JavaScript has an inbuilt Array method map that iterate the values of an Array
persons.map(person => person["salary"] + 1000)
var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":"1500" }];
var mutatedPersons = persons.map(function(obj){
return {name:obj.name,salary:parseInt(obj.salary) + 100};
})
console.log(mutatedPersons);
try:
let persons = persons.map((person) => {person['salary'] = parseInt(person['salary']) + 1; return person})
If you have an array of primitives, you can use this function:
function mapInplace<T>(arr: T[], callback: (v: T, i: number) => T) {
for(const [i, v] of arr.entries()) {
arr[i] = callback(v, i)
}
}
Example usage:
mapInplace(weights, w => w / total)
There's no return value since it's mutating the array.

spread operator converting objects to array

I'm trying to convert a data structure like this:
data = {
0:{A:a},
1:{B:b},
2:{C:c},
}
into a structure like this:
[
{0:{A:a}},
{1:{B:b}},
{2:{C:c}},
]
Using the spread operator like this: [...data] returns any empty array.
I also tried [{...data}]
Is there a way to use the spread operator to get the desired result? Also, why doesn't this approach work?
"Is there a way to use the spread operator to get the desired result?" Short answer, no. (see below for alternate solution to what you're trying to accomplish)
"Also, why doesn't this approach work?"
It doesn't work because according to the MDN docs
"The Rest/Spread Properties for ECMAScript proposal (stage 3) adds spread properties to object literals. It copies own enumerable properties from a provided object onto a new object."
Like the docs say, according to the "Rest/Spread Properties proposal", you can't spread object properties onto an array, objects will always spread their properties onto a new object. Likewise, arrays will not spread onto an object, they will only spread onto a new array.
Alternative solution:
You can do this fairly easily with Object.keys().map(). Object.keys() will get an array of the keys of the object, and Array.map() will map them into an array of the desired structure, like so:
var data = {
0:{A:"a"},
1:{B:"b"},
2:{C:"c"},
}
var result = Object.keys(data).map(function (key) {
return { [key]: data[key] };
});
console.log(result);
You can use Object.entries to get [key, value] pairs, and map them to an array of objects using computed property names:
const data = {
0:{A: 'a'},
1:{B: 'b'},
2:{C: 'c'}
};
const result = Object.entries(data).map(([key, value]) => ({ [key]: value }));
console.log(result);
I'm afraid you cant use to spread operator like in your example, however you can produce the desired output with reduce.
data = {
0:{A:'a'},
1:{B:'b'},
2:{C:'c'},
}
let resArr = Object.keys(data).reduce((arr, e) => {
arr.push({[e]: data[e]});
return arr;
}, []);
console.log(resArr);
let data = ['Uzbek sila', 'Hikmatbet', 'Aslamboi']
let spread = [...data]
console.log(spread)

Trying to sort {key: object} pairs by object property with Javascript and Underscore

{
"TR":{
"total_fans":1848,
"country_name":"Turkey"
},
"US":{
"total_fans":1097,
"country_name":"United States"
},
"DE":{
"total_fans":915,
"country_name":"Germany"
},
"MX":{
"total_fans":1148,
"country_name":"Mexico"
},
"RS":{
"total_fans":359,
"country_name":"Serbia"
},
"IT":{
"total_fans":798,
"country_name":"Italy"
}
}
I would like to sort this based on fans. For instance, in this case it should be "TR", "MX", "US"... and so on using JS and Underscore if that helps.
Thanks!
JavaScript objects are unordered, so you won't be able to sort or otherwise define a specific order.
You could convert the properties to an Array, and sort that instead, but that's about as close as you'll get.
Since it seems you want to sort by number of fans, you can do this.
Object.keys(myObject)
.sort(function(a,b) {
return myObject[a].total_fans - myObject[b].total_fans;
}).forEach(function(key) {
console.log(myObject[key]);
});
To support IE8 and lower, you'll need patches for Object.keys and Array.prototype.forEach.
And as Bruce Lim noted, this is an ascending sort. To descend, swap the operands of the - operator after the return.
return myObject[b].total_fans - myObject[a].total_fans;
No underscore is necessary if you don't care about IE < 9. Given your structure being in a variable obj:
var sorted = Object
.keys(obj)
.map(function(key) {
// Put the country in the item?
obj[key].country = key;
return obj[key];
})
.sort(function(a, b) {
if (a.total_fans > b.total_fans) {
return -1;
} else if (b.total_fans > a.total_fans) {
return 1;
}
return 0;
});
This will give you an Array sorted with the items having the most fans being first, and the country code added to each item in case you need that (omit if unnecessary).
Try sorting with an array:
var array = [];
// copy object to array
for (var x in yourObject) array.push([x, yourObject[x].total_fans]);
// sort array
array.sort(function(item1, item2) { return item1[1] - item2[1] });
// now get the keys
var keys = [];
for (var i = 0; i < array.length; i ++) keys.push(array[i][0]);
// keys = ["RS", "IT", "DE", "US", "MX", "TR"]
Note that the key-getting step could be easier with map if you don't care about old IEs.
UPDATE: Thanks to a heated but useful exchange with Doorknob, I realized that he had an excellent point about being careful about modifying Object.prototype. I modified the answer to make the extension of Object.prototype less intrusive by making the new method non-enumerable. Thanks, Doorknob!
UPDATE 2: I realize now that the OP's wisely selected correct answer has the right of it. Object.keys does exactly what my ownProps method (see below) does, and it's a standard (though you still have to polyfill in IE8). I would just delete this answer since cookie monster nailed it, but I think this is a useful discussion and even though ownProps is redundant with Object.keys, it might teach someone...something.
I wrote a convenience function called ownProps which returns all the property names of an object (excluding properties in the prototype chain). It's a more functional way of doing what Doorknob is suggesting above:
// retrieves all of the properties of an object as an array,
// omitting properties in the prototype chain; note that we use
// Object.defineProperty so we can make this non-enumerable, thereby
// making a safer modification of Object.prototype
Object.defineProperty(Object.prototype,'ownProps',{
enumerable: false,
value: function() {
var props = []; for( var prop in this ) {
if( this.hasOwnProperty( prop ) ) props.push( prop );
}
return props;
}
});
Armed with this, you can sort your object using a custom sort function:
var obj = {"TR":{"fans":1848,"country_name":"Turkey"}/*...*/}
var sortedKeys = obj.ownProps().sort(function(a,b){
return obj[a].fans - obj[b].fans;
});
Solution: http://jsfiddle.net/az7XN/
lets say that whole your object of fans is a fansObj:
for(prop in fansObj){
console.log(prop, fansObj[prop]);
}
output example: TR, {"fans":1848,"country_name":"Turkey" }
before for circle declare array variable and push there each obj:
var arr = [];
for(prop in fansObj){
console.log(prop, fansObj[prop]);
arr.push(fansObj[prop]);
}
then sort:
arr.sort(function(a,b){parseInt(b['total_fans'],10) - parseInt(a['total_fans'])});
parseInt(obj,10) will accept convert object as decimal to integer.
Now you have sorted array (depending on comparison inside sort function (a-b=desc or b-a=asc)).
Used function:
for..in
Array.prototype.sort()
parseInt()

Categories