I'm studying about JSON and its use cases. Suppose I have a recipe book and I have the following JSON to store recipes (I apologize if anything is wrong before hand, I'm just starting with this)
var recipeBook =
{
recipes:
[
{
name: 'Spaghetti',
ingredients:
[
{
ingredientName: 'Pasta',
requiredAmount: 1,
},
{
ingredientName: 'Tomato Sauce',
requiredAmount: 1,
}
]
},
{
name: 'Cereal',
ingredients:
[
{
ingredientName = 'Cereal Box',
requiredAmount = 1
},
{
ingredientName = 'Milk',
requiredAmount = '1'
}
]
}
]
}
Say I wanted to add a third recipe, or add a new ingredient to a recipe...I'm wondering what is the best option (code-wise) to add new data into this JSON.
I think propably the function you are looking for is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push, for example
recipeBook.recipes.push({
name: 'Soup',
ingredients:
[
{
ingredientNam: 'Water',
requiredAmount: 1
},
{
ingredientName: 'Potatoes',
requiredAmount: 4
}
]
})
And for new ingredients it would be
recipeBook.recipes[0].ingredients.push(
{
ingredientName: 'Salt',
requiredAmount: 1
}
)
I would recomend you to use the name of the recipe as the key in the recipes, I mean:
var recipeBook =
{
recipes:
{
Spaghetti:
{
ingredients:
[
{
ingredientName: 'Pasta',
requiredAmount: 1,
},
{
ingredientName: 'Tomato Sauce',
requiredAmount: 1,
}
]
},
Cereal:
{
ingredients:
[
{
ingredientName: 'Cereal Box',
requiredAmount: 1
},
{
ingredientName: 'Milk',
requiredAmount: 1
}
]
}
}
}
This helps to get the recipe as
var spaghettiRecipe = recipeBook.recipes.Spaghetti
First, JSON vs. JS Objects
JSON isn't to be confused with generic objects in JavaScript. JSON is a "lightweight data-interchange format" that does its best to be both easy for humans to read and edit, yet also easy for computers to parse and generate. One of the main differences is that JSON has stricter syntax requirements than generic JS objects do.
What you've written is simply an object declaration in Javascript, rather than standard JSON. A JSON equivalent to your example data would look like this:
{
"recipes": [
{
"name": "Spaghetti",
"ingredients": [
{
"ingredientName": "Pasta",
"requiredAmount": 1
},
{
"ingredientName": "Tomato Sauce",
"requiredAmount": 1
}
]
},
{
"name": "Cereal",
"ingredients": [
{
"ingredientName": "Cereal Box",
"requiredAmount": 1
},
{
"ingredientName": "Milk",
"requiredAmount": 1
}
]
}
]
}
The specific distinctions here:
All property names are enclosed in double quotes "
All strings are enclosed in double quotes " rather than single '
Trailing commas that aren't followed by another key/ value pair or data structure aren't allowed
You could choose to omit the recipes property and keep just the array without the enclosing object, this would still be valid.
Also worth noting that prop = value is not allowed in either syntax, JS object declaration nor JSON.
A description of the full JSON spec is here if you're interested.
Manipulating JS Data
You asked what the best option code-wise is to add data to this JSON. Since JSON is just a format, it's not actually super relevant here, since it's primarily used for data storage and transport, and not for manipulation.
To work with data stored as JSON in Javascript, you can parse it in with the JSON.parse(string) method, which will read a JSON string and return an equivalent Javascipt object. You can then use standard object/ array manipulation methods and techniques on that resulting object, in this case using push() to add a new recipe onto the array.
Here is some sample code to add data to the object
//add recipe
recipeBook.recipes.push(newRecipe);
//add ingredient to recipe by name
var recipe = recipeBook.recipes.find(r => r.name == recipeName);
if (recipe) recipe.ingredients.add(newIngredient);
Also your Cereal object is using ingredientName = 'Cereal Box', which is invalid javascript syntax. You need to use : as in your spaghetti object.
Instantiating an object involves creating an object programmatically. I'm simplifying the code to make it easy:
var book = {}
function Recipe(name, ingredients) {
this.name = name;
this.ingredients = ingredients;
}
// instantiate it
var chicken_teriyaki = new Recipe('Teriyaki', ['new one', 'two', 'three'])
var fried_chicken = new Recipe('Fried Chicken', ['chicken', 'potato starch', 'oil'])
// add to book
book.chicken_teriyaki = chicken_teriyaki;
book.fried_chicken = fried_chicken;
// update recipe
book.chicken_teriyaki.ingredients.push('another yummy')
// view result
console.log('the book', book);
Related
I have a scenario where I want to change properties of object in an array. That array is wrapped inside another object.
const defaultData = {
title: "Title",
subtitle: "Subtitle",
books: [
{
bookId: "1",
imageSrc:
"any.png",
name: "Issue",
userOwnsData: true,
panelsCollected: 0,
totalPanels: 123,
link: "https://google.com",
},
],
bgColor: "black",
};
When I spread it like this:
{...defaultData, ...defaultData.books[0], panelsCollected:123} //previously it was 0
then it adds another extra object to parent object but not update it inside first index of books array
How can I just change that panelsCollected property without disturbing whole structure as we are using typescript.
Edit:
We can change it directly accessing the property too as we know index but that comes with a side effect of manipulating original dataset also which we should avoid and only copy needs to be updated.
Thanks
When spreading an object with nested properties with the intention of updating specific properties, think of it in two steps:
Spread the original object to copy it (...)
Redefine the new property values after the spread object
In your example we are doing the following:
Duplicating defaultData and assigning an updated books property (to be defined in the next step)
Duplicating the first book (defaultData.books[0]) and assigning an updated panelsCollected property to it. Then overwriting the existing books property with this updated array item
The result is as follows:
const defaultData = {
title: "Title",
subtitle: "Subtitle",
books: [
{
bookId: "1",
imageSrc:
"any.png",
name: "Issue",
userOwnsData: true,
panelsCollected: 0,
totalPanels: 123,
link: "https://google.com",
},
],
bgColor: "black",
};
const newBook = {
...defaultData,
books: [
{
...defaultData.books[0],
panelsCollected: 123
}
]
}
console.log(newBook)
/*
{
title: "Title",
subtitle: "Subtitle",
books: [
{
bookId: "1",
imageSrc:
"any.png",
name: "Issue",
userOwnsData: true,
panelsCollected: 123,
totalPanels: 123,
link: "https://google.com",
},
],
bgColor: "black",
};
*/
If for example the books property was 1000 items long, you would instead use have to find the specific book in your array using an array method (e.g. find / findIndex) and update it, e.g.
const bookToUpdateIndex = defaultData.books.findIndex(book => book.bookId === '1')
const updatedBooks = [...defaultData.books]
updatedBooks[bookToUpdateIndex] = {
...updatedBooks[bookToUpdateIndex],
panelsCollected: 123
}
const newBook = {
...defaultData,
books: updatedBooks
}
I think it is creating another parent object because you are using the spread twice. I tried to do 2 console logs. Please let me know if this is the result you are looking for.
console.log({...defaultData['books'][0]['panelsCollected'] = 10})
console.log(defaultData);
Instead of using find and the spread syntax an alternative approach (but not necessarily the most performant) might be to copy the object by stringifying it, and then reparsing that string. And then you can just update the object at the index you need.
const defaultData={title:"Title",subtitle:"Subtitle",books:[{bookId:"1",imageSrc:"any.png",name:"Issue",userOwnsData:!0,panelsCollected:0,totalPanels:123,link:"https://google.com"}],bgColor:"black"};
const copy = JSON.parse(JSON.stringify(defaultData));
copy.books[0].panelsCollected = 123;
console.log(defaultData);
console.log(copy);
I've seen similar questions to this one but in different languages and I am struggling to create a JavaScript equivalent.
I am receiving an object and through a function I want to change the location of one (or more) of the properties. For example,
With the original object of
{
individual: [
{
dob: '2017-01-01',
isAuthorized: true,
},
],
business: [
{
taxId: '123',
},
],
product: {
code: '123',
},
}
I would like to change the location of isAuthorized to be in the first object inside of the business array instead of individual.
Like so
{
individual: [
{
dob: '2017-01-01',
},
],
business: [
{
taxId: '123',
isAuthorized: true,
},
],
product: {
code: '123',
},
}
So far I was trying to create an object that would contain the key name and location to change it to, e.g.
{
isAuthorized: obj.business[0]
}
And then loop over the original object as well as the object with the location values and then set the location of that key value pair.
Basically, in this function I want to see that if the original object contains a certain value (in this case isAuthorized) that it will take that key value pair and move it to the desired location.
What you want can easily be achieved by using loadsh, here's a working snippet of how to restructure based on defined structure map. Extended this example to match what you want.
The example is doing a deep clone, if you are fine modifying the original object then skip that step to avoid the overhead.
// input data
const data = {
individual: [
{
dob: '2017-01-01',
isAuthorized: true,
},
],
business: [
{
taxId: '123',
},
],
product: {
code: '123',
},
};
// the structure change map
const keyMap = {
'individual[0].isAuthorized': 'business[0].isAuthorized'
};
function parseData(data,keyMap) {
const newData = _.cloneDeep(data);
for( let [source,dest] of Object.entries(keyMap) ) {
_.set(newData,dest,_.get(newData,source));
_.unset(newData,source);
}
return newData;
}
console.log(parseData(data, keyMap));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
Note: loadsh's set consider any numeric value as an array index so if you are using a numeric object key then use loadash.setWith. I recommend reading examples in doc for a better understanding.
https://lodash.com/docs/4.17.15#set
This is a nice evening project, but actually i'm stuck with some headache.
All I need is a function like this example:
result = set("itemCategories[0].items[0].name", "Test")
which should return:
{ itemCategories: [
{
items: [ {name: "Test"} ]
}
}]
...and in case of the given attribute "itemCategories[1].items[2].name" this result:
{ itemCategories: [
null,
{
items: [
null,
null,
{name: "Test"}
]
}
}]
Use lodash#set:
result = lodash.set({}, "itemCategories[0].items[0].name", "Test")
If you are asking about the vanilla JavaScript Set method then you could do this.
/* this is what you are trying to get.
{ itemCategories: [
{
items: [ {name: "Test"} ]
}
}]
*/
var mySet = new Set(); // your set object.
Create your data (number, text, string, object, array, null).
ver data1 = 365;
ver data2 = 'Dragonfly';
ver data3 = {name: 'Bobby', age: 20000, job: 'dj'};
Then you just add to that set using its add method.
mySet.add(data1);
mySet.add(data2);
mySet.add(data3);
So to get what you are looking for you would write this.
var itms = {items: [{name: 'test'}]};
mySet.add(itms);
The good thing about set is that is like an array. So you can use forEach.
mySet.forEach( function(val){
console.log(val); // gets all your data.
});
You can even check if a value is in your data using the has method.
mySet.has(365); // true
mySet.has(36500000); as false
JavaScript Set
Using javascript, I want to create an array that has the structure so that it can have all states within the country array. Each state would be an array consisting all the regions within it. And each region would contain all the health clubs within it. Something like this:
[Australia]=>array([NSW]=>array([Sydney]=>array(element1, element2, ...)));
So far I have tried many things including the code below:
$('#search-widget-data li').each(function(){
var dataElement = $(this);
searchWidgetData.push(dataElement.text());
alldata.push([dataElement.prevAll(".state:first").text(),
dataElement.prevAll(".region:first").text());
});
You can use a form like this:
var countries = [
{
name: "Australia",
states: [
{
name: "state1",
regions: [
{
name: "region1",
healthclubs: [
"healthclub1", "healthclub2"
]
},
{
name: "region2",
healthclubs: [
"healthclub1", "healthclub2"
]
}
]
},
{
name: "state2",
regions: [
{
name: "region1",
healthclubs: [
"healthclub1", "healthclub2"
]
}
]
}
]
},
{
name: "USA"
}
];
You may want to store your data structure in the JSON format which will make it easy for storage and working with as a JavaScript object.
You may try this one out.
var regArr[n1][n2];
but you need to specify the length of each inner array.
I want to create a multi-level JSON string with JS.
Scenario
3 countries with 5 grandfathers with 3 kids which whom also have 3 kids that have 5 friends.
I get the data from a external JSON file that looks like this.
{"countries":[
{
"name":"USA",
"grandfathers":[
{
"gFName":"Steve",
"grandfathersKid":[
{
"gFKName": "Linda",
"kid": [{
"name": "Steve JR",
"friends": [{
"name": "Kriss|John|Martin|Steven"
}]
}
]
}
]
}
]
}
]}
And now I want to store some of the countries with people and their relatives and friends in a a new JSON list that looks exactly as the list made in the external json file. I aim to use this "homemade" list later on in the script.
My initial response for this was
var tree = new Array();
tree = {};
var countries = new Array();
countries[0] = "canada";
countries[1] = "USA";
countries[2] = "Mexico";
countries[0][0] = "Steve"; //Lives in Canada
countries[0][0][0] = "Linda"; //Daughter of Steve
countries[0][0][0][0] = "Steve JR"; // Kid of Linda
countries[0][0][0][0][0] = "Kriss"; //Steves Friend
...
$.each(countries...function(index, value){
tree[index].country = value;
$.each(grandfathers...function(key, value){
tree[index].country[key].grandfather = value;
}
And so on, but this is not giving me the result I want. What am I doing wrong? And a more effective way than to take each of everything?
Third edit...
Is this the sort of thing you're trying to do?
var countries = $.map(oldCountries || [], function(country) {
return {
name: country.name,
people: $.map(country.grandfathers || [], function(gpa) {
return {
name: gpa.gFName,
children: $.map(gpa.grandfathersKid || [], function(parent) {
return {
name: parent.gFKName,
children: $.map(parent.kid || [], function(kid) {
return {
name: kid.name,
friends: kid.friends
};
})
};
})
};
})
};
});
I wasn't sure what to do with the friends node. Should that be normalized into something more useful, or do you want to leave it alone?
This Fiddle demonstrates the technique.
I think we'd need to know more about your requirements. But several thing I see here are:
You declare tree and initialize it as an Array, then immediately reinitialize it as an
empty object
You are not creating the intermediate nodes here, such as tree[index] but just assuming
that they exist.
You are trying to assign the country[key] property of an object, using the dot-property
access.
Can you supply the countries structure and the grandfather's structure. And are they nested?
And finally, what would you like for the output format? The code above hints at it, but it's still a little fuzzy.
Edit
So are you trying to achieve a structure something like this?:
var countries = [
{
name: "Canada",
people: [
{
name: "Steve",
children: [
{
name: "Linda",
children: [
{
name: "Steve, Jr.",
friends: [
{
name: "Kriss"
}
//, more friends
]
}
//, more grandchildren
]
}
//, more parents
]
}
//, more grandparents
]
}
//, more countries
];
May be this jsfiddle can help you to get started?
And here is an example derived from your code.
Sounds like a homework, so I'll try to point you in the right direction. I think you are confusing objects and arrays. You could use a "country" object and a "person" object. A country object should have an array of person objects, as inhabitants. Person objects can have an array of person objects as descendants. Add a method like "addDescendant", which creates a new person under a person. From There you can build the structure as you like. Here is some pseudo code:
countries = [];
function Country(name){ this.name = name; this.population = [];}
function Person(kids){this.descendants = []; this.addDescendant = function(){...};
//loop from 1 to kids and add descendants as "new Person"
}
person = new Person(3);
country1 = new Country("MyCountry1");
// now add people to country1.population
countries.push(country1);
The final structure should look something like this:
countries = [
{ name: "country 1",
people: [{ name: "Steve"},
{name: "Clara", descendants: [{name: "Clara's daughter"},
{name: "Clara's son"}]
]}
},
{ name: "country 2",
people: [{}, {} ...]
}
];