This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 6 years ago.
I am a beginner and, as I began to increase the complexity of JSON, I started to confuse myself by accessing it. How do I access the following JSON? I would like to access the value and keys of the services
{
employers:{
Mike: {
old: 20,
services:{
cut : {
value : 10
}
hair_straightening : {
value: 20
}
}
}
Penny: {
old: 20,
services:{
cut : {
value : 10
}
hair_straightening : {
value: 20
}
}
}
}
}
Everyone has to start somewhere :)
I'm not sure if I completely understand what you're after, but here goes...
First off, it looks like your notation on your object is subtly off when listing your keys. For example, between Mike and Penny you should have a comma separating each of those keys in the larger employers object.
Something like:
employers: {
Mike: {
...
}, //need a comma here
Penny: {
...
}
}
Also, within each of those employers, there should be a comma between the keys for cut and hair_straightening.
services: {
cut: {
value: 10
}, //need a comma here
hair_straightening: {
value: 20
}
}
Now to your actual question...
To get the keys for each of the services, you can use Object.keys(). This function would get you they keys for a given employer. Then you can also grab the values from inside that same function. (Note this is for only one employer; you'd just want to iterate over both and use this same function on each)
function getServices(employer) {
var services = employer.services;
var servicesKeys = Object.keys(services);
var serviceValueMatrix = [];
servicesKeys.forEach(function(service) {
serviceValueMatrix.push([service, employer.services[service].value])
})
return serviceValueMatrix;
}
// assuming you had var yourJSON = { employers: {...} }
// getServices(yourJSON.employers.Mike);
// returns [["cut",10], ["hair_straightening",20]]
Also, given that your JSON object is already in key:value format, you could probably skip the last set of objects in the format of value: 10, value: 20 etc, and instead just make the last tier of your object something like:
services:{
cut: 10,
hair_straightening: 20
}
Then you could just grab services.cut and services.hair_straightening.
Full code below for clarity:
const yourJSON = {
employers: {
Mike: {
old: 20,
services: {
cut: {
value: 10
},
hair_straightening: {
value: 20
}
}
},
Penny: {
old: 20,
services: {
cut: {
value: 10
},
hair_straightening: {
value: 20
}
}
}
}
}
function getServices(employer) {
var services = employer.services;
var servicesKeys = Object.keys(services);
var serviceValueMatrix = [];
servicesKeys.forEach(function(service) {
serviceValueMatrix.push([service, employer.services[service].value])
})
return serviceValueMatrix;
}
console.log(getServices(yourJSON.employers.Mike));
// returns [["cut",10], ["hair_straightening",20]]
your json should look something like this:
{
employers:[
{
name: "Mike",
age: 20,
services: [
{name:"cut",value:10},
{name:"hair_straightening",value:20}
]
},{
name: "Penny",
age: 20,
services: [
{name:"cut",value:10},
{name:"hair_straightening",value:20}
]
}
]
}
You can use Object.keys to get the keys of an object as an array, then you can loop through that nicely.
// in this case json is a variable representing your parsed data
Object.keys(json).map(function(key) {
console.log(json[key])
return json[key].services
})
That would give you an array of services objects.
Related
I have an array with over 50 entries in the form of objects, which I would like to save depending on the Item ID so that I can then apply certain calculations to them. For example, I would like to add up the time of all entries with the Item Id "Las5241Wz".
Since the array can change dynamically, I can't analyze it manually. How can I separate the data beforehand according to their Item ID and push them into new arrays? The real Array contains up to 16 objects with the same ID.
var data= []
data = [
//...objects
{
itemId: "Las5241Wz",
time: 10
},
{
itemId:"Bos1239Zf",
time: 11
},
{
itemId:"Las5241Wz",
time: 15
},
{
itemId:"Bos1239Zf",
time: 21
}
//...more objets
]
The solution for this should look like this:
var Item1 = [
{
itemId: "Las5241Wz",
time: 10
},
{
itemId:"Las5241Wz",
time: 15
},
]
var Item2 = [
{
itemId:"Bos1239Zf",
time: 11
},
{
itemId:"Bos1239Zf",
time: 21
}
]
Here is another solution that builds an object with the properties "item1", "item2" and so on from the given object:
const data = [
//...objects
{
itemId: "Las5241Wz",
time: 10
},
{
itemId:"Bos1239Zf",
time: 11
},
{
itemId:"Las5241Wz",
time: 15
},
{
itemId:"Bos1239Zf",
time: 21
}
//...more objets
]
console.log(
Object.values(
data.reduce((o,e)=>((o[e.itemId]=o[e.itemId]||[]).push(e),o),{}))
.reduce((o,c,i)=>(o["item"+(i+1)]=c,o),{})
);
This is a "one-liner" and for that reason not that easy to read. So, probably not the version you would put into your production code.
Unless you have a performance reason to keep the lists separately, the answer is that you can just store the list of ids as a Set and use array.filter when you want to get the list that is just for that id
Set s = new Set();
for (let i = 0; i < data.length; i++) {
s.add(data[i].itemId);
}
var createFilterFunc(id) {
return function(elem) {
return elem.itemId == id;
}
}
var items = data.filter (createFilterFunc("Las5241Wz"));
I have an object array as follows:
products = [
{
id: 1,
title: "Product 1",
specifications: {
price: 1.55,
discount: 15,
attributes: [
{
l1: 100,
l2: 80
height:200,
weight: 15,
parameters: [
{
id: 199199 // this is how I identify the parameter
size: 185 // this is what I want to change
}, ...
]
}, ...
]
}
}, ...
]
... and an array of changes to parameters I want to apply, for example: change size to 189 where product.specifications.attributes.parameters.id == 199199.
I'd like to do this without flattening any elements as they are part of a Vue.js data structure, it will break the reactivity.
How could I do this? I am open to using Underscore or lo-dash
This looks ugly, but it is effective:
To make it more dynamic, let's use variables: identifier will be your '199199' value and new_size for the '189' value.
methods: {
updateRecord: function(identifier, new_size) {
this.products.map(function(product) {
product.specifications.attributes.map(function(attribute) {
attribute.parameters.map(function(parameter) {
if (parameter.id == identifier) parameter.size = new_size;
})
});
});
}
}
Here is a working fiddle: https://jsfiddle.net/crabbly/eL7et9e8/
_.forEach(products, function(product) {
_.forEach(_.get(product, 'specifications.attributes', []), function(attribute) {
_.set(_.find(attribute.parameters, {id: 199199}), 'size', 189);
});
});
I believe what you want is underscore's findIndex() - http://underscorejs.org/#findIndex. Once you find which element in the array you want to apply the changes to (comparing the nested id to what you are looking for) you can then make the change to that particular element.
So I have an array that contains objects with different attributes and I want to know how I can make multiple arrays with objects with the same attributes of the whole array.
I want to go from this
[
{name:”test”, place:”country”},
{name:”walkAndEat”, Long=100,Lat:15,Location:”place name”},
{name:”test2”,place:”Europe”}
]
To
[
{name:”test”, place:”country”},
{name:”test2”,place:”Europe”}
]
[
{name:”walkAndEat”, Long:100,Lat:15,Location:”place name”}
]
If you see objects being equal as having the same properties, you can keep the keys as (stringified) indices in a collection object and check if a properties-key already exists:
var arrcoll = {};
function add(o){
var keys = JSON.stringify(Object.keys(o).sort());
var arr = arrcoll[keys];
if(arr)
arr.push(o);
else
arrcoll[keys] = [o];
return arr;
}
This can be done on the fly or on a pre existing array as shown in this Fiddle
Suppose you have a list of objects that have different properties like so:
var placesOrPeople = [
{ name: 'Seymour Skinner', occupation: 'Principal' },
{ name: 'Kwik-E-Mart', lat: 23, long: 100 },
{ name: 'Sideshow Bob', occupation: 'Comic Foil' },
{ name: 'Flaming Tyre Yard', lat: 12, long: 88 },
{ name: 'Joe Quimby', occupation: 'Mayor' }
];
And you want them sorted into separate lists like so:
places = [
{ name: 'Kwik-E-Mart', lat: 23, long: 100 },
{ name: 'Flaming Tyre Yard', lat: 12, long: 88 }
];
people = [
{ name: 'Seymour Skinner', occupation: 'Principal' },
{ name: 'Sideshow Bob', occupation: 'Comic Foil' },
{ name: 'Joe Quimby', occupation: 'Mayor' }
];
You can use the built-in Array.filter command like so:
var places = placesOrPeople.filter(function(currentPlaceOrPerson) {
if (currentPlaceOrPerson.occupation !== undefined) {
// it must be a person, since locations don't have occupations
return true;
} else {
return false;
}
});
var people = placesOrPeople.filter(function(currentPlaceOrPerson) {
if (currentPlaceOrPerson.lat !== undefined && currentPlaceOrPerson.long !== undefined) {
// it must be a place, since people don't have co-ordinates
return true;
} else {
return false;
}
});
Javascript Objects are not set types, they are dynamic, meaning you can change them during execution.
JavaScript is a loosely typed or a dynamic language. That means you don't have to declare the type of a variable ahead of time. The type will get determined automatically while the program is being processed. That also means that you can have the same variable as different types:
var anything = arrayOfAnyOtherType[0];
is valid... If you loop your source array and populate it, you can define any behavior to each object
I want to use linq.js to group the following data by date.
data2 = [{
"date": 1399298400.0,
"adId": 1057946139383,
"impressions": 1000000
}, {
"date": 1399298400.0,
"adId": 3301784671323,
"impressions": 535714
}...... etc.
];
Here's my attempt:
var linq = Enumerable.From(data2);
data2 = linq.GroupBy(function (x) {
return x.date;
}).Select(function (x) {
return {
date: x.Key(),
impressions: x.Sum(function (y) {
return y.impressions | 0;
})
};
}).ToArray();
However, It's not working correctly because the sum of all the impressions before and after the GroupBy are close but not identical.
What is the correct way to use group by in linq.js in this case?
Here's an example in fiddle with full dataset here which alerts the total impressions before and after using the GroupBy.
Solution
You can do this by passing a callback as the third parameter like this:
var grouped = Enumerable.from(dataArray).groupBy("$.person", null, (key, g) => {
return {
person: key,
likes: g.sum("$.likes | 0")
}
}).toArray()
Explanation
In groupBy, the third parameter allows you to modify the results before emitting:
In JS, the bitwise or operator (a single pipe |) returns the first value if it exists, otherwise it returns the second one. Without it, trying to sum an undefined value with a real one, will return NaN
undefined + 1 // NaN
Without | 0, the result would look like this:
This example uses shorthand syntax, but if you prefer anytime you see a string with a dollar sign, you can replace it with the lambda syntax like this (they both do the same exact thing):
// Shorthand
.Select("$.impressions")
// Lambda
.Select(function (x) { return x.impressions })
Working demo with Stack Snippets:
var dataArray = [
{
person: "james",
likes: 100
},
{
person: "james",
likes: 250
},
{
person: "kyle",
likes: 300
},
{
person: "kyle"
//,likes: 450
}
];
var grouped = Enumerable.from(dataArray).groupBy("$.person", null, (key, g) => {
return { person: key, likes: g.sum("$.likes | 0") }
}).toArray()
console.log(grouped);
<script src="https://unpkg.com/linq#3.2.0/linq.js"></script>
Further Reading:
SO - linqjs group by with a sum
I am the author of the open source project http://www.jinqJs.com. You can easily do that in jinqJs like this:
jinqJs().from(data2).groupBy('date').sum('impressions').select();
Let me know if I can be of anymore help.
You might try to group by date.toString(). Might be safer due to how JS evaluates dates equality
Alternatively, people coming into this question might have zero to a lot of buy in using linq.js.
If you're already pulling it in, go for it, but if this is the first couple real use cases for it, it's worth noting that you can accomplish the same thing in vanilla js:
For this data:
var dataArray = [
{ person: "james", likes: 100 },
{ person: "james", likes: 250 },
{ person: "kyle", likes: 300 },
{ person: "kyle" }
];
You can build an object with properties for each key / person and keep summing the values of those props
var obj = dataArray.reduce((acc, cur) => {
acc[cur.person] = (acc[cur.person] || 0) + (cur.likes || 0)
return acc
}, {})
If you want that to be an array of objects, you can convert from an object to array like this
var array = Object.entries(obj).map(entry => {
return { person: entry[0], likes: entry[1] }
})
When running the following query using the root directory of my Firebase, extra nodes are added.
Query:
fire.set({
"users":[
{
"0":[
{
"email":"foobar#gmail.com",
"snake":[
{
"highScore":"15"
}
]
}
]
}
]
});
Result
I get the same result when formatting the data in a json file and importing it directly using the Firebase web interface. Did I miss something in the documentation perhaps?
The issue here is that any time you use array syntax, i.e. [ ... ], you're creating an "array" in Firebase, which we do by just creating an object with numeric keys (i.e. 0, 1, 2, ...).
So if you do:
ref.set({ a: 5 });
The resulting object will be:
{ a: 5 }
But if you instead do:
ref.set([{a: 5}, {b: 6}]);
You'll get:
{
'0': { a: 5 },
'1': { b: 6 }
}
So if you just remove the square brackets from the data you're setting, e.g.:
fire.set({
"users": {
"0": {
"email":"foobar#gmail.com",
"snake": {
"highScore":"15"
}
}
}
});
The resulting data in the web interface should match your data exactly.