I have the following object being returned. I am counting a list of names by reading from a json file and storing the results in a new object.
{
ted: 501,
jeff: 40,
tony: 90
}
The following function creates an object with the names as properties and the count as their values.
function countNames(json){
var count = {};
for (var i = 0, j = json.length; i < j; i++) {
if (count[json[i].name]) {
count[json[i].name]++;
}
else {
count[json[i].name] = 1;
}
}
return count;
}
I need to create an array of objects that generate a result like this.
[
{
name: 'ted',
total: 501
},
{
name: 'jeff',
total: 40
}
{
name: 'tony',
total: 90
}
]
I am not sure what the best approach and most efficient way of achieving this is. Any help is appreciated.
Consider this following Javascript snippet:
for (var item in obj) {
result.push({
name: item,
total: obj[item]
});
}
Working DEMO
Output:
[
{
"name":"ted",
"total":501
},
{
"name":"jeff",
"total":40
},
{
"name":"tony",
"total":90
}
]
I don't understand how your code example relates to the question, but this turns the data in the first format into the last format:
var output = Object.keys(input).map(function(key) {
return {
name: key,
count: input[key]
}
});
it uses functional programming style, which usually leads to cleaner code.
JSBin
Related
I want to merge two arrays of objects. Those objects have the same structure, but one of them is missing the hide property. I want to copy the value of hide property from one object to the other that is missing this property. The important part is that I don't want to mutate any of these arrays!
The first array looks like this (notice that there is hide property):
let first_array = [
{
name: 'John',
age: 40,
hide: true,
childs: [
{
name: 'Alice',
age: 20,
hide: false,
childs: [
{
name: 'Mike',
age: 2,
hide: true
}
]
}
]
},
{
name: 'Peter',
age: 40,
hide: true,
childs: [
{
name: 'Andrew',
age: 20,
hide: true,
childs: [
{
name: 'Jessica',
age: 2,
hide: true
}
]
}
]
}
]
The second array looks almost the same! The only thing missing is hide property.
let second_array = [
{
name: 'John',
age: 40,
childs: [
{
name: 'Alice',
age: 20,
childs: [
{
name: 'Mike',
age: 2,
}
]
}
]
},
{
name: 'Peter',
age: 40,
childs: [
{
name: 'Andrew',
age: 20,
childs: [
{
name: 'Jessica',
age: 2,
}
]
}
]
}
]
Now, I want to create new array with where within each object there is hide property.
I know how to do this recursively in the imperative way, but unfortunately I'm mutating data - which I don't want to do.
function getHideProperty(first, second) {
for (let i = 0; i < second.length; i++) {
for (let j = 0; j < first.length; j++) {
if (second[i].name === first[j].name) {
second[i].hide = first[j].hide
if (second[i].childs) {
second[i].childs = getHideProperty(first[j].childs, second[i].childs)
}
}
}
}
return second
}
Now I can create new array with merged objects:
const newArray = getHideProperty(second_array, first_array)
Now, every object in second_array has hide property. But I mutated the array :(
How to achieve such result without mutating the array?
You'll need to:
Create a new array to store the new information, and return that
Deep-copy second[i] to store in the new array, prior to modifying anything
For #2, choose your favorite answer from What is the most efficient way to deep clone an object in JavaScript?
For #1, very roughly (see comments):
function getHideProperty(first, second) {
const result = []; // Our result array
for (let i = 0; i < second.length; i++) {
const secondEntry = result[i] = deepCopy(second[i]); // The deep copy, and let's avoid constantly re-retrieving second[i]/result[i]
for (let j = 0; j < first.length; j++) {
if (secondentry.name === first[j].name) {
secondentry.hide = first[j].hide
if (secondEntry.childs) {
// Could be more efficient here, since the entries in `childs` are already copies; left as an exercise to the reader...
secondEntry.childs = getHideProperty(first[j].childs, secondEntry.childs)
}
}
}
}
return result;
}
This is not meant to be an all-singing, all-dancing solution. It's meant to help you along the way. Note the deepCopy placeholder for your preferred solution to #2. :-)
If you do something like the above (nested loops) and find that it's a performance problem, you can create a Map of the entries in first keyed by their names, and then look them up in the map when looping through second (rather than the nested loop). The complexity is only useful if you run into a performance problem with the simple nested loops solution.
This is a functional approach that doesn't mutate any of the original arrays or their items:
function getHideProperty(first, second) {
return second.map(function(item) {
var corresponding = first.find(function(searchItem) {
return searchItem.name === item.name;
});
return Object.assign({},
item,
{ hide: corresponding.hide },
item.childs
? { childs: getHideProperty(item.childs, corresponding.childs) }
: {}
);
});
}
My object:
"hockey": {
stats: {
skaters: {
regular: [
{name: "stat1", key: "statkey1"}
{name: "stat2", key: "statkey2"}
{name: "stat3", key: "statkey3"}
]
},
goalies: {
regular: [
{name: "stat1", key: "statkey4"}
{name: "stat2", key: "statkey5"}
{name: "stat3", key: "statkey6"}
]
}
}
}
My code:
var stats = [];
var key = "";
for (position in sport.stats) {
for (stat_group in position) {
for (stat in stat_group) {
key = stat.key;
stats[key] = true;
}
}
}
I'm trying to use the above code to grab the property key from each object located within sport.stats.position.stat_group. Each sport has a different number of positions and stat groups, hence the triple for loop. I'm not getting any console errors it just isn't grabbing the key at all and the iterator variables aren't evaluating to objects but integers.
Here's what I want the resulting stats object to be:
{
"statkey1": true,
"statkey2": true,
"statkey3": true,
...
}
Hope you guys can help! Thanks!
The JS for...in loop iterates through the keys, not values. If you want to iterate an object fully, you can do so like this:
for (key in sports.stats) {
var position = sports.stats[key];
for (group_key in position) {
var stat_group = position[group_key];
for (stat_key in stat_group) {
stat_group[stat_key] = true;
}
}
}
For...in in javascript gives you the key of the object, not the value.
According to your logic, this is what you meant to do:
var stats = {};
var key = "";
for (position in sport.stats) {
for (stat_group in sport.stats[position]) {
for (stat in sport.stats[position][stat_group]) {
key = sport.stats[position][stat_group][stat].key;
stats[key] = true;
}
}
}
Essentially, I want to implement the following:
var categories = [];
var products = // some array of product objects
products.map(function(value) {
if(categories.indexOf(value.Category === -1)) categories.push(value.Category);
});
As result, categories array contains unique list of product categories.
I feel that there should be a better way to do it, but nothing comes to mind.
If there isn't then probably there is no point to use map() in the first place. I could do as simple as
var categories = [];
var products = // some array of product objects
for (var i = 0; i < products.length; i++) {
if(categories.indexOf(products[i].Category === -1)) categories.push(products[i].Category);
}
UPDATE for those who insist it's a duplicate of "how to make an array unique" question. I saw that post, and for my situation I don't think it applies. I don't have an array of values that I need to make unique. I have an array of objects and I need to build an array of unique values. The difference might be subtle - but to get to the use case of that topic I would build a non-unique array and then make it unique. Seems even worse than my original solution
you can use reduce instead of map
var products = [{Category:'vegetable', price: 1}, {Category:'fruits', price: 2}];
var categories = products.reduce(function(sum, product) {
if(sum.indexOf(product.Category) === -1){
sum.push(product.Category);
}
return sum;
}, []);
Update: A solution with Array.prototype.reduce()
var products = [{ Name: 'milk', price: 2.50, Category: 'groceries' }, { Name: 'shirt', price: 10, Category: 'clothing' }, { Name: 'apples', price: 5, Category: 'groceries' }],
categories = products.reduce(function (r, a) {
if (!~r.indexOf(a.Category)) {
r.push(a.Category);
}
return r;
}, []);
document.write('<pre>' + JSON.stringify(categories, 0, 4) + '</pre>');
map all the values of the object categories out first, then use filter to dispose of the duplicates.
var products = [
{ category: 'A' },
{ category: 'B' },
{ category: 'A' },
{ category: 'D' }
];
var categories = products.map(function (e) {
return e.category;
}).filter(function (e, i, a) {
return a.indexOf(e) === i;
}); // [ "A", "B", "D" ]
DEMO
Follow the Below SO Answer:
How to get distinct values from an array of objects in JavaScript?
var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
if( flags[array[i].age]) continue;
flags[array[i].age] = true;
output.push(array[i].age);
}
My problem is to merge an object and an array of objects.
Here's my object:
{
model1: ["model1-coupe", "model1-hatchback", "model1-cabriolet"],
model2: ["model2-coupe","model12-hatchback","model2-cabriolet"],
model3: ["model3-coupe","model4-hatchback","model4-cabriolet"]
}
Here's my array of objects:
[
{image: "/path/to/image/model1.jpg"},
{image: "/path/to/image/model2.jpg"},
{image: "/path/to/image/model3.jpg"}
]
I'd like to merged them like that:
[
{
image: "/path/to/image/model1.jpg",
model1: ["model1-coupe", "model1-hatchback", "model1-cabriolet"]
},
{
image: "/path/to/image/model2.jpg",
model2: ["model2-coupe", "model2-hatchback", "model2-cabriolet"]
},
{
image: "/path/to/image/model3.jpg",
model3: ["model3-coupe", "model3-hatchback", "model3-cabriolet"]
}
]
How do I do that? I can use either JavaScript or Underscore.
Thanks in advance
EDIT: see how the merging result would actually be:
[
{
image: "/path/to/image/model1.jpg",
cars: ["model1-coupe", "model1-hatchback", "model1-cabriolet"]
},
{
image: "/path/to/image/model2.jpg",
cars: ["model2-coupe", "model2-hatchback", "model2-cabriolet"]
},
{
image: "/path/to/image/model3.jpg",
cars: ["model3-coupe", "model3-hatchback", "model3-cabriolet"]
}
]
for (var i = 0, key; i < modelArray.length; i++) {
modelArray[i].cars = modelObject[Object.keys(modelObject)[i]];
}
This will loop the array and add the property to the object. This assumes that the object properties are all in the proper order.
In each iteration, it loops up which property it's up to using Object.keys(). Then, it sets that key of the object in the array to that property of the model object.
You can do something like
var models = {
model1: ["model1-coupe", "model1-hatchback", "model1-cabriolet"],
model2: ["model2-coupe", "model12-hatchback", "model2-cabriolet"],
model3: ["model3-coupe", "model4-hatchback", "model4-cabriolet"]
}
var images = [{
image: "/path/to/image/model1.jpg"
}, {
image: "/path/to/image/model2.jpg"
}, {
image: "/path/to/image/model3.jpg"
}]
for (var key in models) {
if (models.hasOwnProperty(key)) {
var idx = +key.match(/\d+$/)[0] - 1;
images[idx][key] = models[key]
}
}
console.log(images)
try something like this:
var result = [];
var models = {
// your models object
}
var images = [
// your images array
];
for(var i=0, key in models) {
var model = models[key];
var image = images[i++];
result.push( { image: image, model: model } );
}
I have an object that looks like this
myList: {
id1:{
ts:'2010-01-12T00:51:00',
name:"roger"
},
id2:{
ts:'2011-01-12T05:22:00',
name: "Tom"
},
id3:{
ts:'2013-01-12T11:32:00',
name:"Jack"
}
}
I know objects cant be sorted so i wanted to know how i can generate an array of just the keys,which are sorted according to the key "ts". I want this in descending order.
So the array for the above object will be [id3,id2,id1]
once i have this array i can make operations like this where arr is sorted array and myList is the object
for(var i=0:i<arr.length;i++)
{
alert(myList[arr[i]].name);
}
var keys = Object.keys(myList).sort(function(a, b) {
if (myList[a].ts == myList[b].ts) {
return 0;
}
return myList[a].ts < myList[b].ts ? 1 : -1;
});
console.log(keys);
jsfiddle: http://jsfiddle.net/pWq2L/
Explanation:
First you export keys from the object: Object.keys(myList)
You sort using custom comparison function and in it you refer to the .ts attribute of the compared values. a and b are the keys of the compared elements
References:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Given this data structure, and assuming there is a good reason for it to not be an array:
var myList = {
id1: {
ts: new Date('2010-01-12T00:51:00'),
name: 'Roger'
},
id2: {
ts: new Date('2011-01-12T05:22:00'),
name: 'Tom'
},
id3:{
ts: new Date('2013-01-12T11:32:00'),
name: 'Jack'
}
}
We can create and sort an array like so:
var arr = [];
Object.keys(myList).forEach(function(item) {
arr.push(myList[item]);
});
arr.sort(function(a, b) {
return a.ts > b.ts ? -1 : a.ts < b.ts ? 1 : 0;
})
One way to extend zerkms solution is to work with an object augmented with smart properties. The versatile reduce comes in handy:
var result = keys.reduce(function(arr, k, i) {
var item = {
key: k,
index: i,
value: myList[k]
};
arr.push(item);
return arr;
}, []);
//result:
[
{ index:0 , key: id3, value: {...} },
{ index:1 , key: id2, value: {...} },
{ index:2 , key: id1, value: {...} }
]
reduce is part of the ECMAScript 5th edition; so you may fill in some gap with a polyfill.
http://jsfiddle.net/pWq2L/3/