I have a function which get a json as parameter, build another json with some values from given json and return builded json.
function getMyJSON(json) {
var result = {
lastUpdate: "",
legends: null
};
result.legends = (new Array(json.legends.length)).fill({
name: null,
rgb: null,
values: null
});
for (let j = 0; j < json.legends.length; j++) {
result.legends[j].name = json.legends[j].name;
result.legends[j].rgb = json.legends[j].rgb;
result.legends[j].values = (new Array(10)).fill(0);
console.log(result.legends[0].name); //PRINT ONLY FIRST ELEMENT
}
console.log(result.legends);
return result;
}
The problem appear after for loop is done. All result.legends have the same value from the last json.legends
Here is how output look:
The legends.name of first element(result.legends[0].name) is changed after every loop.
At the end, all legends.name from result are equal with the last legends.name from json. Why?
I found on google that it is something about variable scope, but I can't figure it out how to do this.
You need independent objects inside of the array. Array#fill takes the same object reference and this leads to the same result in each object.
Instead of this, you could create a new array with Array.from and map new objects with the second parameter for a callback.
result.legends = Array.from(
{ length: json.legends.length },
_ => ({ name: null, rgb: null, values: null })
);
#NinaScholz has described the problem and solved it, however as I mentioned in the comments on the question you can improve and simplify the logic by using map():
var obj = {
legends: [{
name: 'foo',
rgb: 'C00'
},{
name: 'bar',
rgb: 'FFF'
},{
name: 'fizz',
rgb: 'CCFFCC'
},{
name: 'buzz',
rgb: '000000'
}]
}
console.log(getMyJSON(obj));
function getMyJSON(o) {
return {
lastUpdate: "",
legends: o.legends.map(function(item) {
return {
name: item.name,
rgb: item.rgb,
values: (new Array(10)).fill(0)
}
})
};
}
Related
I'm trying JS. I have used only Python recently.
And there is a problem, look at this example:
var myCharacter = {
name: "character",
avatarImage: "avatar.png",
experience: 1,
level: 1,
items: {helmetSlot: blank, armorSlot: blank}, //'items' will be changed into 'inventory'
backpack: [],
strength: 0,
//It is corrent 'method'?
takeOff: function(slot) {
if (this.backpack.length < 10) { //setting backpack length
this.backpack.push(this.items.slot)
this.items.slot = blank
}
else {}
}
}
myCharacter.takeOff(helmetSlot) //why it doesn't work? I know it did't declared but ... You know I want to use 'word' helmetSlot to make block 'takeOff' way I want, I have used to do things like this in python
Problem is in method takeOff. Can you explain me how to use it like I can do it in Python?
Here is full code: https://pastebin.com/NP1KLPie
I know I have done it wrong, but how to use it the way I want?
this.items.slot is looking for a property literally named "slot". Instead you can use square brackets to use a variable property name: this.items[slot] (passing a string value into slot.)
var myCharacter = {
name: "character",
avatarImage: "avatar.png",
experience: 1,
level: 1,
items: { helmetSlot: "helmet", armorSlot: "armor" }, // using strings instead of the `blank` variable, just so the results will be visible
backpack: [],
strength: 0,
takeOff: function(slot) {
if (this.backpack.length < 10) {
this.backpack.push(this.items[slot]) // not this.items.slot
this.items[slot] = "empty" // string instead of `blank` again
} else {}
}
}
myCharacter.takeOff("helmetSlot") // pass a string, not a bare variable name
console.log(myCharacter) // check the results
in your code values you are using for helmetSlot and armorSlot is not valid .Because in javascript you can can assign one of the below values to variablesor properties of objects
a) String ('' or 'somevalue')
b Number
c) undefined
d) null
e) {}
f) function (){
....
}
var myCharacter = {
name: "character",
avatarImage: "avatar.png",
experience: 1,
level: 1,
items: {helmetSlot:'', armorSlot: ''}, //'items' will be changed into 'inventory'
backpack: [],
strength: 0,
//It is corrent 'method'?
takeOff: function(slot) {
if (this.backpack.length < 10) { //setting backpack length
this.backpack.push(this.items.slot)
this.items.slot = undefined;
}
else {}
}
};
console.log(myCharacter);
myCharacter.takeOff(10);
I'm learning to manipulate JSON data and I am stuck trying to figure out how to cajole the following JSON into what I want as shown below:
Any pointers to function/terms/concepts that I should learn for this sort of problem would be greatly appreciated! Thanks
JSON object
{
car: 1,
van: 5,
cat: 99999999999999999999999
}
Desired outcome:
items: [
{ "type": "car", "value": "1"},
{ "type": "van", "value": "5"},
{ "type": "cat", "value": "99999999999999999999999"}
]
You can use a combination of Object.entries and Array.prototype.map:
const obj = { car: 1, van: 5, cat: 99999999999999999999999 };
let list = Object.entries(obj) // [["car",1],["van",5],["cat",99999999999999999999999]]
.map(x => ({ type: x[0], value: x[1] }));
console.log(list);
Or, with some destructuring:
const obj = { car: 1, van: 5, cat: 99999999999999999999999 };
let list = Object.entries(obj)
.map(([type, value]) => ({ type, value }));
console.log(list);
The callback to map:
([type, value]) => ({ type, value })
Expects an array as parameter: [type, value]. The first value in that array is assigned to type, the second one to value.
Then we use a shorthand form to set these values in our returned object:
=> ({ type, value })
I'm a beginner. I tried to solve the problem and this is the best I can come up with, tested in Node.js 10.
const obj = {"car": 1, "van": 5, "cat": 999999}
const items = []
for (let key in obj) {
items.push({"type": key, "value": obj[key]})
}
console.log(items)
One thing I am slightly confused about is the difference between for..in vs for..of, I'm currently looking into it.
Object.keys will return:
['car', 'van', 'cat'];
On this array you can use Array's map function which creates a new array with the results of calling a provided function on every element in the calling array.
var a = {
car: 1,
van: 5,
cat: 99999999999999999999999
}
m = Object.keys(a).map((v)=>{
return {
type: v,
value: a[v]
}
})
console.log(m);
#GustavMahler hope you understand. To learn more about array functions you should look map, reduce and filter.
This one uses object.keys
let js = {car:1, van:5, cat:9999}
Object.keys(js).map( x => ({type: x, value: js[x] }) )
[ { type: 'car', value: 1 },
{ type: 'van', value: 5 },
{ type: 'cat', value: 9999 } ]
I am filtering and mapping the array objects that looks like this:
taxonomy:Object
data:Array[2]
0:Object
id:377
name:"Buss"
slug:"buss"
type:"post_tag"
1:Object
My function looks like this:
let tag = this.article.taxonomy.data.filter(function( data ) {
return data.type.includes('tag')
}).map(function(obj) {
return obj.name;
});
return tag;
What I am just wondering is there a way to get from the map function just the string name value, since now it returns ["Buss"], so that don't need to use the index at the end of the function:
return tag[0]
It sounds like you want find, not filter, if you're looking only for one result. find returns the first entry for which the callback returns a truthy value, or undefined if it never does. So:
const obj = this.article.taxonomy.data.find(data => data.type.includes('tag'));
const tag = obj && obj.name; // Note the guard in case no entry matched
return tag; // Will be the name or `undefined`
(Note that I've assumed you can use an arrow function, as you're using let. If not, just replace it with a function function.)
Live Example:
const taxonomy = {
data: [
{
id: 375,
name: "A",
slug: "A",
type: "nope"
},
{
id: 376,
name: "B",
slug: "B",
type: "nor-this"
},
{
id: 377,
name: "Buss",
slug: "buss",
type: "post_tag"
},
{
id: 378,
name: "C",
slug: "C",
type: "blah"
}
]
};
const obj = taxonomy.data.find(data => data.type.includes('tag'));
const tag = obj && obj.name;
console.log(tag);
I have following 2 arrays of object. I want to convert the array from 1st format to 2nd format, i.e, data values stored from int to string, but unable to find a convinient way. Can someone help?
Please, help out with a way to do so easily by Javascript.
Try this:
The map() method creates a new array with the results of calling a provided function on every element in this array.
var test = [{
id: 0,
title: 0
}, {
id: 1,
title: 1
}, {
id: 2,
title: 2
}, {
id: 3,
title: 3
}];
var newArray = test.map(function(item) {
return {
id: item.id.toString(),
title: item.title.toString()
};
});
console.log(newArray);
Use map, for example:
tootSetArray.map(function(item){
return {
id: '' + item.id,
title: '' + item.title
};
});
The map() method creates a new array with the results of calling a
provided function on every element in this array.
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] }
})