Javascript JSON unknown behavior - javascript

I am attempting to set the value of a JSON object property, however when I run Object.property = value the entire JSON object is replaced with the string 229,.
In the following code block:
for(var i=0; i<config["profiles"].length; i++){
profile = config["profiles"][i];
out = {
"name":profile["name"],
"version":profile["version"].replace(/_/g, "."),
"mods":null,
"saves":null
}
console.log(out)
out.mods = getMods(profile);
console.log(out)
console.log(getSaves(profile))
out.saves = getSaves(profile);
console.log(out)
profiles.push(out);
}
return profiles;
The first 2 console.log(out) calls return a proper JSON object as expected.
The console.log(getSaves(profile)) prints the following:
[ { name: 'Hard Career ',
mode: 'CAREER',
funds: '275,520',
science: '229',
reputation: '721',
flights: '20' },
{ name: 'Sandbox ',
mode: 'SANDBOX',
funds: 0,
science: 0,
reputation: 0,
flights: '12' } ]
However, the print directly after out.saves = getSaves(profile) prints the following: 229,.
To make things even more complicated, this only occurs on one item in the config["profiles"] array.
If anyone has possible solutions to the problem, I would love to hear them. If you need more information about the code, I'll see what I can do.
Thanks in advance!

You should declare your variables with something like let profile =
If you don't do this profile and out will be global variables, which means each time through your for loop you are reassigning a single shared global variable. If you have other code that is also doing this it can very difficult to keep track of. Changing your code to:
let profile = config["profiles"][i];
let out = {
"name":profile["name"],
"version":profile["version"].replace(/_/g, "."),
"mods":null,
"saves":null
}
should help.

Related

How to combine JS Objects with duplicate key/values

sorry if this is a easy question, I am just having a hard time trying to figure out how I would tackle this problem.
For example, I have 2 Objects as below:
cont oldCar = {
model: 'Honda',
notes: {
id: 1,
timestamp: 2000,
text: 'is old'
}
}
cont oldCar = {
model: 'Toyota',
notes: {
id: 1,
timestamp: 4000,
text: 'is new'
}
}
I want to try and combine the above two objects. I know they have same key's so I wanted to merge the values of each key if they are the same. Such as:
mode: 'Honda / Toyota'
I tried the following:
let merged = {...obj1, ...obj2};
But this will merge both objects but it only retains the values from the right object. I was trying to do a for loop and add check if the key is same in both objects then combine the values together but I keep getting lost and it is hard to visualise. If someone could help me understand how i can create an for loop to start the comparison that would help me in completing the rest.
To do this merge, perhaps you could do some array-reduce, it will also work with a list of unspecific size:
let array = Array(oldCar1,oldCar2)
let result = array.reduce((a,b)=> {
let r = Object.assign({},a)
r.notes = Object.assign({},r.notes)
if (a.model != b.model) {
r["model"] = a.model + " / " + b.model;
}
if (a.notes.text != b.notes.text) {
r.notes.text = a.notes.text + " / " + b.notes.text;
}
// ...
return r;
})
What exactly do you want to achieve? Is it only the merging of model prop or something else?
Do you have more than two objects, is the amount of objects dynamic? If there are only two objects you can do that without any loops.
const merged = {
model: `${firstCar.model} / ${secondCar.model}`,
// etc...
};
But as I said before - if the amount of objects is not constant then you'd need a map that would:
go through each car
try and find a match by ID from other cars
if there's a match return a merged result, if there's no match return the object as it is
Let me know what exactly are your needs here.

Transform data into nested JSON

I am working with javascript for the first time, and I have an array of around 35000 objects, where each object looks like this:
{city: 'city1', buildingtype: 'buildingtype1', day: 1, hour: 1, energy: 59}
I want to sort this data into a "nested" JSON object, with cities as the first object. For each city, I then need to sort by buildingtype, and for each buildingtype I want an array with the energy, sorted by time.
I have added an image here which might explain it a bit better:
I have begun with something like this
let transformed_data = {}
$:{
datapoints.forEach((d)=> {
if(!(d.city in transformed_data)){
transformed_data[city] = {};
}
}
)
}
Would this be a good way to move forward?
Edit:
Thanks to some nice help this is the solution I found, where I used that the data was already sorted to my advantage.
let transformed_data = {}
$:{
datapoints.forEach((d)=> {
if(!(d.city in transformed_data)){
transformed_data[d.city] = {};
}
if (!(d.buildingtype in transformed_data[d.city])){
transformed_data[d.city][d.buildingtype] = [];
}
transformed_data[d.city][d.buildingtype].push(d.total_heating_energy)
}
)
}
The resulting object can be found here: Final_result

Is it efficient to create multiple keys pointing to the same object?

I have an array of countries, where each country have three properties.
const countries = [
{ name: "Poland", alpha: "POL", code: "616" },
{ name: "Hungary", alpha: "HUN", code: "348" },
// and so on...
]
Later on I want to easily access each country by any of these properties.
I was thinking of reducing this array to an object that would have three keys for each country pointing to the very same country object.
Something like this:
const countriesObject = countries.reduce((object, country) => {
const { name, alpha, code } = country;
// Create individual country object
object[name] = { ...country };
// Reference just created country object
object[code] = object[name];
object[alpha] = object[name];
return object;
});
In the end I could access each country object either by its name, code or alpha.
countriesObject["Poland"] // →
countriesObject["POL"] // → The same object
countriesObject["616"] // →
My question is, would it be considered good practice, or there are some better ways to achieve the same or similar result?
Thank you!
That's fine, as all of those keys, as you correctly noted, will be pointing to the same object. The biggest problem that I see here is that it's easy to reduce readability of the code by using this approach. Let's say we have this fragment:
console.log( countriesObject[id] );
The question is, what is id here? Is it full country name? or just alpha? or code? You might just not care, true, but if you do, consider giving them additional structure:
const countriesObject = countries.reduce((object, country) => {
const { name, alpha, code } = country;
const countryCopy = { ...country };
// it makes sense to place them on the same line to show the intent
object.name[name] = object.code[code] = object.alpha[alpha] = countryCopy;
return object;
}, { name: {}, code: {}, alpha: {} });
Another potential issue is that you won't be able to drop the countries easily from this object; it's not enough to delete just a single key pointing to it, you'll have to go and remove all three. But that doesn't seem to be a big thing here; this looks more like a dictionary.
You can indeed write it like this:
var countries = {[
"poland": {
alpha: "POL", code: "616"
},
"hungary": {
alpha: "HUN", code: "348"
}
]}
Accessing each country like this:
var poland = countries.poland;
This, in turn, produces more readable code:
var alpha = countries.poland.alpha;
instead of
var alpha = countries[0].alpha;
But there is no set preference.
Docs

Getting first object inside JSON

I'll cut straight to the chase. I'm getting a json object with another object inside of it like so:
function getName(summonerName, region) {
LolApi.Summoner.getByName(summonerName, region, function(err, summoner) {
if(!err) {
console.log(summoner);
}
});
}
However, the result of this call is (let's stay summonerName is "tetsii"):
{ tetsii:
{ id: 51520537,
name: 'tetsii',
profileIconId: 23,
summonerLevel: 23,
revisionDate: 1408307600000
}
}
Now, I can access the id's and stuff with "console.log(summoner.tetsii.id)" for example, but because the summonerName (in this case "tetsii") can be anything, I prefer not to do it like so. So, my question is: how to access the first object inside a JSON or is there another way? And no, I can't get an array in this case afaik.
I would like to note that I've tried "console.log(summoner.summonerName.id)", but that doesn't yield results as summonerName is a string.
Thanks everybody
EDIT: Got the answer. By simply using summoner[summonerName].id I am able to grab the id. Thanks everyone for answers!
-Tetsii
By using Object.keys. For example, if you know that summoner will only have a single top-level key, you can get it with:
var summonerName = Object.keys(summoner)[0];
Obligatory browser support notice: IE < 9 does not support this out of the box, but you can use a polyfill provided in the MDN page as a compatibility shim.
There is no order in objects, so there's no guarantee you'll get the object you think, but using Object.keys and shift() you can do
var first = summoner[Object.keys(summoner).shift()];
If there is no way to return it as an array, the best idea is to iterate over the object properties as documented in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
The most important part is:
for (var prop in summoner) {
console.log("summoner." + prop + " = " + summoner[prop]);
}
Tested in console:
var summoner = { tetsii:
{ id: 51520537,
name: 'tetsii',
profileIconId: 23,
summonerLevel: 23,
revisionDate: 1408307600000
}
};
yields:
summoner.tetsii = [object Object]

How can I call several variables that begin with the same letters and how can I call all variables that contain a certain value?

I need the following two codes
1) A code to select all variables that begin with "example"
2) A code to select all variables that have "true" as value for "available"
example1= {price:1000, size: 1000, available:true}
example2= {price:2000, size: 2000, available:false}
example3= {price:3000, size: 3000, available:true}
example4= {price:4000, size: 4000, available=true}
This is what I want to achieve with code one. As there are a lot of variables I need a quick way of doing it:
var allexampleprices=[example1.price, example2.price, example3.price, example4.price]
With the second code I want to get an array with all the names of the variables that contain the value "false"
Any help appreciated!
All of these are the exact same thing, assuming you're not in a function:
var myVar = 7;
window.myVar = 7;
window["myVar"] = 7;
Therefore, you can access any global variable (a variable defined outside a function) by using the window[ insertString ] method. If you wanted to search through every property on the window object to find one called example, you'd do:
for( var k in window ){
if(/example/.test(k)){
var myExample = window[k];
// Do stuff
}
}
I would HIGHLY recommend against this method, though, for many reasons. To start, it's a horribly bad practice to put anything in the global scope. Variables will start colliding all over the place on big projects. Also, the window object has soooooo many properties that searching through all of them is a horrible performance drain.
Having said all of that, I've devised an example of what you should do, including the helper functions to do it:
var objects =
{
example1:
{
price: 1000,
size: 1000,
available: true
},
example2:
{
price: 2000,
size: 2000,
available: false
},
example3:
{
price: 3000,
size: 3000,
available: true
},
example4:
{
price: 4000,
size: 4000,
available: true
}
}
function filter(obj, comparator){
var list = [];
for(var k in obj){
if(obj.hasOwnProperty(k)){ // fix for IE
if(comparator(obj[k], k, obj)) list.push(obj[k]);
}
}
return list;
}
function isExample(obj, key){
if(/^example/.test( key.toLowerCase() )) return true;
}
function isAvailable(obj){
if(obj.available) return true;
}
/**
* And here's how you use it
*/
var examples = filter(objects, isExample);
var available = filter(objects, isAvailable);
var availableExample = filter(examples, isAvailable);
The filter function returns an array of all of the matching objects.
--- EDIT ---
You want it to say the names of the objects in the console. I'm assuming what you mean is that the console currently shows [object, object, object, object]. There are two ways to do this:
(1) Put the name in the object itself
example1:
{
name: "example1",
price: 1000,
size: 1000,
available: true
}
(2) Capture the name in the filter operation
var names = [];
var examples = filter(objects, function(obj, name){
if(/^example/.test( name.toLowerCase() )){
names.push(name);
return true;
}
});
console.log(names);
I do like below if all variables are in global scope
var passedElements = [];
for(var i = 1, l = 100 /* Maximum number of variable */ ; i < l; i++){
if(window['example' + i]){
var temp = window['example' + i];
if(temp.available === true){
passedElements.push(temp);
}
}/*
else{
// Dont break the loop here, if any variable is missing in between
two variables it will fail. Eg : Example1, Example3.. 2 is missing.
}*/
}
console.log(passedElements);
I hope it will help.
It's seems like follwing line is generated by some logical code:
var example1= {price:1000, size: 1000, available:true}
Why dont you simply store the vaiable names in another array that should give you the solution of Q-1.
Then you can easily travers through all of the vaiables (array) to find the vairables that have "true" as value for "available"

Categories