I am trying to access varaibles in the object returned by an API but it's inside of another object "0" in this case
Screenshot of console
enter image description here
JavaScript code
fetch('https://dad-jokes.p.rapidapi.com/random/joke', options)
.then(response => response.json())
.then(response => {
console.log(response)
document.getElementById("joke").innerText = (response.body)
})
.catch(err => console.error(err));
I can't write console.log(response.0.setup) and console.log(response.body.0.punchline) because JavaScript won't let me. How would i access the setup and punchline from JS?
I don't have an API key to test this out myself, but it sounds like you can solve this with bracket notation. Any object property can be accessed like object['property'] which is equivalent to the dot notation object.property for properties with string names. In this case your property is named with a number so the bracket notation is the only way to access it.
This should achieve what you're trying to do
document.getElementById("joke").innerText = (response[0])
and you can chain off of that to access the setup and punchline like
const punchline = response[0].punchline
Related
I am using Map
because I want to store an object as a key.
My question is - can I access a map the same way I would access a plain object?
For example:
let m = new Map();
let obj = {foo:'bar'};
m[obj] = 'baz';
console.log(m[obj]);
is this supposed to work correctly as is, or do I need to use the get/set methods of a Map?
The reason I ask is because if I need to use get/set it forces to me to carefully refactor a lot of code.
Here is a real life example of code that may need to be refactored:
// before (broker.wsLock was plain object)
function addWsLockKey(broker, ws, key) {
let v;
if (!( v = broker.wsLock[ws])) {
v = broker.wsLock[ws] = [];
}
if (v.indexOf(key) < 0) {
v.push(key);
}
}
// after (broker.wsLock is a Map instance)
function addWsLockKey(broker, ws, key) {
let v;
if (!( v = broker.wsLock.get(ws))) {
v = [];
broker.wsLock.set(ws, v);
}
if (v.indexOf(key) < 0) {
v.push(key);
}
}
is there some way to set v on the same line as the set() call?
If you want access to the actual values of the Map object, then you have to use .get() and .set() or various iterators.
var m = new Map();
m.set("test", "foo");
console.log(m.get("test")); // "foo"
Regular property access on a Map such as:
m["test"] = "foo"
just sets a regular property on the object - it does not affect the actual map data structure.
I imagine it was done this way so that you can access the Map object properties separately from the members of the Map data structure and the two shall not be confused with one another.
In addition, regular properties will only accept a string as the property name so you can't use a regular property to index an object. But, map objects have that capability when using .set() and .get().
You asked for a "definitive" answer to this. In the ES6 specification, you can look at what .set() does and see that it operates on the [[MapData]] internal slot which is certainly different than the properties of an object. And, likewise, there is no where in that spec where it says that using normal property access would access the internal object [[MapData]]. So, you'll have to just see that normal property access is describe for an Object. A Map is an Object and there's nothing in the Map specification that says that normal property access should act any different than it does for any other object. In fact, it has to act the same for all the methods on the Map object or they wouldn't work if you happened to put an item in the Map with the same key as a method name. So, you're proof consists of this:
A simple test will show you that property access does not put anything in the Map itself, only a regular property.
The spec describes a Map as an object.
The spec describes how .get() and .set() operate on the internal slot [[MapData]].
There's nothing in the spec that says property access on a Map object should work any different than it always does.
If property access did access the MapData, then you would not be able to access methods if you happened to put a key in the Map that conflicted with a method name - that would be a mess if that was the case.
I came across this example in a MDN doc, for example:
class Search1 {
constructor(value) {
this.value = value;
}
[Symbol.search](string) {
return string.indexOf(this.value);
}
}
If I pull up node, and run just the line included as part of the object literal, it doesn't work:
> Symbol.search
Symbol(Symbol.search)
> [Symbol.search]
[ Symbol(Symbol.search) ]
> [Symbol.search]('somthing')
TypeError: [Symbol.search] is not a function
I think I've also seen this syntax in a few other places, like e.g. in the react docs:
handleChange(event) {
this.setState({ [event.target.id]: event.target.value });
}
Is this just a use of destructuring syntax? It doesn't seem like it.
brackets are used when you have variable as key and not a plain string.
const obj = {
"someId": 'abc',
};
const e = {
target: {
id: "someId"
}
};
console.log(obj[e.target.id]);
Apart from above mentioned, it is also used to access the numeric keys (Just like array) and when key is computed. See - https://javascript.info/object#square-brackets
Turns out, that's just part of the spec.
It looks a bit like array de-structuring, but it's not.
In the case of [event.target.id], you're assigning the value that event.target.id points to be a key in the object passed to setState(). If you tried to do this without the brackets ([]), it would not work, not how you expect anyway.
In the case of [Symbol.search](string), here you're using the Symbol.search symbol (see symbols) as a key which is dynamically evaluated immediately to its actual, unique value. The dynamic evaluation is allowed because this value becomes the key in an object literal definition. The value which the key points to is a function being defined here, which takes string as its first and only parameter, and operates on that. This is a hook for allowing an object to define how it behaves when used as a parameter, in this case to the .search() function. See here.
Thanks for #randomSoul's answer, for completing it I might say that braces also make you to have a string key with spaces like below:
const myOBJ = {
'my key': 'my assigned String Value'
}
Then you can call that key value pair with this braces syntax like:
console.log(myOBJ['my key'])
This is rarely used in JavaScript, but the main purpose of using braces for getting the value from object literal is for getting dynamically computed keys of object. Like that you have an object that each key is represented user id, and you based on that you want to decide to get the specific user id that you got from your url params or somewhere else then you would be able to get user data like below:
console.log(lastFiveUserData[myUserId].age)
The code is a general toggle handler for a component state. I cant seems to figure out why the first set of code create a new key name property while the second set of code use the accepted parameter.
controlToggle = (property) => {
this.setState({property: !this.state.property})
}
controlToggle = (property) => {
this.setState({[property]: !this.state.property})
}
You make use of [] while setting or getting a dynamic object key. If you do not provide the key within [] it will use the variable name as the key within the object which in your case is property
So for instance
controlToggle = (property) => {
this.setState({property: !this.state.property})
}
The above code will set the state with key as property
While the correct way is
controlToggle = (property) => {
this.setState({[property]: !this.state[property]})
}
I think that's because in JavaScript, for a key-value pair like {XXX: YYY}, it will automatically treat the first XXX as the key/property name of the value, so you have to add the [] brackets to "escape" that pattern to use the variable's value as the key instead of treating it as a string essentially.
More specifically, the stuff inside the [] brackets will be treated as normal JavaScript code and be "calculated"
I have been playing with some code over the last few days and I am trying to get more into functional programming but I am at a road block. I don't understand how to handle an object. Essentially I have an object that I want to add key value pairs to. I understand in function programming you don't reassign you just make a new object with the added key value pair. I was thinking about putting the object in some type of container like 'Box'
const Box = x =>
({
map: f => Box(f(x)),
fold: f => f(x),
})
And then using the 'Box' and adding all my key value pairs, something like
const formData = obj =>
Box(obj)
.map(s => s.key1 = document.getElementById("value1").value)
.map(s => s.key2 = document.getElementById("value2").value)
.fold(s => s)
var emptyObj = {};
const test = formData(emptyObj);
I noticed that 'emptyObj' will have both key value pairs but 'test' will not. I know that I am either completely missing it or just not understanding the process. I have been watching
https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript
and it is great along with the ebook but there must be something that I am missing. Any help would be greatly appreciated.
I think you need to do something like this:
const formData = obj =>
Box(obj)
.map(s => ({ ...s, key: document.getElementById("value1").value }) )
.map(s => ({ ...s, key2: document.getElementById("value2").value } ))
.fold(s => s)
Note that I would not have used map and fold rather monadic names then and bind.
The problem is that your map callback functions return the string (the result of the assignment is the right hand side), not the object whose property they assigned to. You would need to write
Box(obj)
.map(s => (s.key1 = document.getElementById("value1").value, s))
.map(s => (s.key2 = document.getElementById("value2").value, s))
.fold(s => s)
to get test === emptyObject (and have both properties on it).
That said, I don't understand what this "Box" is good for. It would be much simpler if you had just written
const formData = () => ({
key1: document.getElementById("value1").value,
key2: document.getElementById("value2").value,
});
I understand in function programming you don't reassign you just make
a new object with the added key value pair.
Immutability isn't purely equivalent to functional programming, but I expect from this statement that your desired result (which you do not explicitly state in your post!) is for emptyObj to remain its original empty object, and for test to be a separate object with the key1 and key2 properties.
The problem is that you shouldn't be using objects for this. Not because they're in opposition to functional programming, but because objects aren't true dictionaries, and don't have easy tools for doing this sort of thing. You want to use a Map instead. Maps are designed to be dictionaries, and more importantly, are iterators, which allow some shorthand and reduced cognitive load.
const Box=(x)=>({
map: (key,value)=>Box((new Map(x).set(key,value))),
clear: (key,value)=>Box((copy=>(copy.delete(key),copy))(new Map(x))
),
fold: f=>f(new Map(x)), // EDITED to prevent leaking the original map
})
const formData=obj=>(
Box(obj)
.map('key1',document.getElementById("value1").value)
.map('key2',document.getElementById("value2").value)
.fold(s=>s)
)
const clearData=obj=>(
Box(obj)
.clear('key1')
.clear('key2')
.fold(s=>s)
)
const emptyObj=new Map()
const test1=formData(emptyObj)
const test2=clearData(test1)
console.log(emptyObj) // empty
console.log(test1) // both keys
console.log(test2) // empty again
I added the ability to immutably clear data as well as map data, to show how easy it is. I used a weird construct in the clear method because Map.prototype.delete returns a success boolean and not the Map, so I used a comma operator. These evaluate left to right and returns the last value.
If you want to get into immutables without being strictly functional, then there are immutable class libraries available for Javascript that would provide immutable Maps, or it would be easy to subclass one.
EDIT
OP has mentioned the need to do this in objects for JSON intercommunication. One can use a helper function to convert the Map to JSON:
let map=new Map([['key1','value1'],['key2','value2']])
const mapToJson=map=>(
'{'+
[...map].map(([key,value])=>(
'"'+key+'":'+(value===undefined ? 'null' : JSON.stringify(value)))
).join(',')+
'}'
)
console.log(mapToJson(map)) // {"key1":"value1","key2":"value2"}
I had to add a specific check for undefined because JSON.stringify(undefined)=='undefined', but the JSON spec doesn't recognize undefined and uses null instead.
This assumes string keys, but maps can use anything as a key. You can add error checking, or just assume you're only passing a Map with string keys.
I've retrieved a JSON response from an external API and one of the variable names begins with a # character, so the JSON variable looks like this #text. Unfortunately angular doesn't know how to handle this variable, is there any way delete the # so that the data assigned to the identifier is usable.
In order to reference a property that does not follow the rules for a properly formatted identifier, you must use bracket notation instead of dot notation for the property accessor:
var object = JSON.parse('{"#text":"https://lastfm-im...png","size":"extralarge"}')
console.log(object['#text'])
you cam modifide the response - loop throw it wite map- and chenge the object key -see here:
JavaScript: Object Rename Key
You can use square bracket notation instead of dot notation. like (Object mutation is highly not recommended)
//to access it u can use square bracket notation like k["objectname"]
let k = {"#test":"some data"}
alert("data is "+k["#test"])