I like to think I understand JavaScript, but I found something unexpected today and I was hoping someone could explain to me why it happens.
Take this code
var animalData = {
cow:"cow",
sheep:"sheep",
getCow:function()
{
return this.cow;
},
animalList:[
{
animalId:this.cow,
label:"This is a cow"
},
{
animalId:this.sheep,
label:"This is a sheep"
}
]
};
console.log(animalData.getCow());
console.log(JSON.stringify(animalData.animalList,null," "))
The output is not what I was expecting. Calling animalData.getCow() results in "cow" just as you would expect. But it's what gets return by the second console.log that confuses me.
[
{
"label": "This is a cow"
},
{
"label": "This is a sheep"
}
]
In other words, the object removes the animalId property entirely from the objects defined. I was expecting this
[
{
"animalId":"cow",
"label": "This is a cow"
},
{
"animalId":"sheep",
"label": "This is a sheep"
}
]
And I could understand maybe this
[
{
"animalId":undefined,
"label": "This is a cow"
},
{
"animalId":undefined,
"label": "This is a sheep"
}
]
But why does the animalId property get removed entirely?
Can anyone explain what's going on under the surface to cause this behaviour? I'm guessing that the this keyword does not work because the properties are undefined when it is invoked, but why does it remove the property entirely?
NB: I'm not looking for a workaround, that's easy enough to do - just interested in why it happens.
JSFiddle here
At the point the object is initialised, this refers to the outer context, which won't have cow and sheep properties. As you thought, that will result in the animalIds being undefined.
JSON.stringify does certain things with undefined properties, namely:
If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array).
Which is why you don't see them.
First of all, you are correct your last example, this is what you are trying to stringify:
[
{
"animalId":undefined,
"label": "This is a cow"
},
{
"animalId":undefined,
"label": "This is a sheep"
}
]
And, because those values are undefined JSON.stringify simply omits them.
Why the values above are undefined is because the this keyword in this.cow refers to the current scope, which is actually the window Object as it is not inside any other function.
Why it makes sense to omit keys with undefined values? Because whether they exist or not, if you try to access object.key you will get the correct value: undefined
Related
I tried to work it out and ask who I could but no one gave me an answer that would work. can anyone advise me?
obj[i].["name of thing in object"]
this is array:
[
{
"name": "DISBOARD#2760",
"id": "3020508723832240"
},
{
"name": "Gumernus#0122",
"id": "7814540349956106"
}
]
Assuming your object looks a bit like
let obj = [
{ nameOfThingInObject: 'first'},
{ nameOfThingInObject: 'second'}
];
To retrieve the nameOfThingInObject you can do two things:
Use . to access the property
Use [] to access the property
Your example in the question seems to try both, which will result in an error. To retrieve the value, you can thus do:
let firstMethod = obj[0].nameOfThingInObject;
let secondMethod = obj[0]['nameOfThingInObject'];
Based on the initial object Contact, I have to create a second object. Sometimes the Contact object will not have certain properties. I wanted to know if there was a way to print value of ConsentDt using a method within an object.
I know that I could simply code "ConsentDt": Contact.CommPhoneConsentDt and if that key is not available, ConsentDt will not be printed in the final output. However, sometimes determining if certain keys should be printed are a little bit more complicated, e.g. only include Email in the final object if EmailConsentDt == 'Y'. I also know that I could write functions outside of the object to make these determinations, but I wasn't sure if there was a way to keep the logic all in one object. Thanks in advance!
let Contact = {
"Name": "Kyle Gass",
"CommPhone": "+9-999-999-9999",
"Email": "tenacious#d.org",
"CommPhoneConsentCd": "Y",
"CommPhoneConsentDt": "2019/8/1",
"EmailConsentCd": "N"
}
let Communications = {
"PhoneInfo" : {
"PhoneTypeCd": "Cell",
"PhoneNumber": Contact.CommPhone,
"PhoneNumberValidInd": "N",
"ContactPreferenceType": "Primary",
"ConsentCd": Contact.CommPhoneConsentCd,
"ConsentDt": function(Contact) {
if (Contact.hasOwnProperty("CommPhoneConsentDt")) {
return Contact.CommPhoneConsentDt
} else {
return
}
}
}
}
console.log(Communications.PhoneInfo.ConsentDt);
//I want ConsentDt of 2019/8/1 to print out
You can use the get syntax on the object:
Communications = {
"PhoneInfo" : {
"PhoneTypeCd": "Cell",
"PhoneNumber": Contact.CommPhone,
"PhoneNumberValidInd": "N",
"ContactPreferenceType": "Primary",
"ConsentCd": Contact.CommPhoneConsentCd,
get "ConsentDt"() {
if (Contact.hasOwnProperty("CommPhoneConsentDt")) {
return Contact.CommPhoneConsentDt
} else {
return
}
}
}
}
console.log(Communications.PhoneInfo.ConsentDt);
ConsentDt of 2019/8/1 is printed out
I have a Loopback moddel that looks like this:
{
"name": "string",
"elements": [
"string"
]
}
Now I want to filter if elements property conatins a certain string.
Something like this:
User.find({
filter: {
where: {elements: $scope.objects[i].id} //doesn't work, I want sth like "element contains $scope.objects[i].id
}}, function (user) {
console.log(user);
});
Warning: This solution was meant to answer the question "how do I filter a list of objects". It was accepted so I can't remove it. I don't know anything about LoopBack which has performance implications I'm not privy to. So please keep searching if you are looking for a "LoopBack" best practice.
This seems like a javascript question to me. The elements property contains an array so you can filter that array with filter().
yourModel = { // <-- Using a plain object for demo.
"name": "string",
"elements": [
"string"
]
}
matchingElements = yourModel.elements.filter(function(elm){ return elm === $scope.objects[i].id});
didMyModelHaveTheElement = matchingElments.length > 0;
I'm studying through the tutorial at http://reactivex.io/learnrx/. I'm on Exercise 19 - Reducing with an Initial Value: Sometimes when we reduce an array, we want the reduced value to be a different type than the items stored in the array. Let's say we have an array of videos and we want to reduce them to a single map where the key is the video id and the value is the video's title.
As far as the tutorial is concerned, I've solved it:
function exercise19() {
var videos = [
{
"id": 65432445,
"title": "The Chamber"
},
{
"id": 675465,
"title": "Fracture"
},
{
"id": 70111470,
"title": "Die Hard"
},
{
"id": 654356453,
"title": "Bad Boys"
}
];
return videos.reduce(function(accumulatedMap, video) {
var copyOfAccumulatedMap = Object.create(accumulatedMap);
copyOfAccumulatedMap[video.id] = video.title; // <-- My solution
return copyOfAccumulatedMap;
}, {});
} // end of overall function
To verify your solution you click, "Run." If it runs correctly then you get to move on to the next exercise. I did and it gave me the next exercise. My test suite tells me differently.
While trying to solve it, I created this test:
it("should be able to reduce to an object with id's for keys", function() {
var output = [{
"65432445": "The Chamber",
"675465": "Fracture",
"70111470": "Die Hard",
"654356453": "Bad Boys"
}];
expect(exercise19()).toEqual(output);
}); // end it
(I got the output from the tutorial.)
The problem I'm having is the test continues to fail:
Expected [ Object({ 654356453: 'Bad Boys' }) ] to equal [ Object({
65432445: 'The Chamber', 675465: 'Fracture', 70111470: 'Die Hard',
654356453: 'Bad Boys' }) ].
So it seems like it's only picking up the final property, the 'bad boys' property, in the test. I'm thinking that, with the way reduce works and Object.create, that the other properties are there, but they're on the prototype. How can I get this test to pass..?
UPDATE:
I fixed this in a pull-request. These tutorial no uses Object.assign, instead of Object.create. It is now testable. :-)
It looks like a known issue with Jasmine toEqual -- it just ignores properties from prototypes. You probably could use something like that in the test:
// ...
expect(exercise19()).toEqual(jasmine.objectContaining({
"654356453": "Bad Boys"
// rest data here
}));
Object.create creates a new object with the prototype of the object specified in the first argument - you are not copying the object at all, you are creating a new object with Object's prototype - i.e. you're doing a long winded var copyOfAccumulatedMap = {}
instead, do this
return videos.reduce(function(accumulatedMap, video) {
accumulatedMap[video.id] = video.title;
return accumulatedMap;
}, {});
I have a JSON object that looks like this:
var json = {
"cj-api": {
"products": [
{
"$": {
"total-matched": "231746",
"records-returned": "999",
"page-number": "1"
},
"product": [ {... // contains lots objects with the data I'd like to access } ]
As noted above, I want to access the product array of objects. I can't seem to do this though. I've tried:
console.log(json['cj-api']['products'][0]['product']);
But I get typeError: Cannot read property 'products' of undefined.
What's the correct way to access the array of product (note, singular product, not products). This data is coming from an external source so I can't alter the hyphen in cj-api.
EDIT: Here's what the raw console log of json looks like:
{"cj-api":{"products":[{"$":{"total-matched":"231746","records-returned":"999","page-number":"1"},"product":[{ << lots of data in here>>
EDIT 2: To further clarify, I got this object by running JSON.stringify(result) after I put some XML into XML2js.
i have tried the following JSON structure:
var json = {
"cj-api": {
"products": [
{
"$": {
"total-matched": "231746",
"records-returned": "999",
"page-number": "1"
},
"product": [
{
"a": "a",
"b": "b",
"c": "c"
}
]
}
]
}
};
with the log statement as:
console.log(json['cj-api']['products'][0]['product']);
And result is as follows:
[Object { a="a", b="b", c="c"}]
Well your way of accessing json is absolutely correct. This is for debugging. Try
console.log(json['cj-api']);
console.log(json['cj-api']['products']);
console.log(json['cj-api']['products'][0]);
console.log(json['cj-api']['products'][0]['product']);
Which ever line returns undefined means your json is broken there.
If this doesn't work then you need to check for other similar keys. Maybe they value you are trying to find is actually undefined.
Maybe you are trying to loop. If you are then check for the condition if (JSONStructure[key]==undefined) console.log("Undefined at position ..."). That is the only way if you have valid JSON.
typeError: Cannot read property 'products' of undefined means that json exists, but json['cj-api'] is undefined. If you are sure that you are using the right variable name, I think this might be a scope issue, where you are using an other variable than you intend to. json might be the json string, instead of the array-like object. Try renaming your variable and see if you still get this problem. Otherwise the string is not automatically parsed for you and you'll have to parse it with JSON.parse( ... ).
Edit:
var json = '{ "me": "be an evil string" }';
console.log( json ); //'{ "me": "be an evil string" }'
console.log( json['me'] ); //undefined
console.log( JSON.parse( json )['me'] ); // 'be an evil string'
Since your question is missing the last
}
]
}
}
and others here changed your example and made it work, did you try to correct it?
If not then I suggest you or the dataprovider correct the structure of the reply.
I have tried below json structure
var json={
"cj-api": {
"products": [
{
"$": {
"total-matched": "231746",
"records-returned": "999",
"page-number": "1"
},
"product": []
}
]
}
}
now json['cj-api']['products'][0]['product'] will work