Fix this test: Functional-Reactive Programming Tutorial - javascript

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;
}, {});

Related

Find specific JSON key and return its value in Javascript

I have some JSON from an API which I am logging in the console.
This is easy for something like the id where I can do this...
let movieID = out.id;
console.log(movieID)
But how can I also return the 'name' of the person whose job is specifically 'Director' from JSON example like this? Basically I need to do something like 'if someone has the JOB of DIRECTOR, log their name to the console.
{
"id": 37291,
"credits": {
"crew": [
{
"name": "John Smith",
"job": "Producer"
},
{
"name": "Mary Jones",
"job": "Director"
}
]
}
}
You can get the list of the crew, like you described above. Then find the first entry in that list where the job equals "Director" and put it in a variable:
let director = out.credits.crew.find(member => member.job == "Director");
You can then log the data, as you did for the movie itself.
Something like this would do literally what you asked which was to console log out the names
credits.crew.foreach((crewMember) => {
if (crewMember.job == 'Director') {
console.log(crewMember.name)
}
})
Use underscore:
_.filter(credits.crew, (key) => key.job === 'Director')
That will give you all the keys with that value. Trust me you should use underscore or lodash because objects can get super tricky and it saves you a ton of time.

Modify an array of objects inside an array of objects in js

Hello developers I'm trying to modify an array of objects inside an array of objects before deploying its result to Redux reducer.
The array is obtained through a request to an endpoint, reason why i must to create an instance of writable copy of it , and then proceed on the process
Lest say i have this array:
allProducts= [
{
"product_type": "Bikes",
"product_imgs": [
{
"id": 5,
"url": "Mountain Bike/Screenshot (200)"
},
{
"id": 6,
"url": "Mountain Bike/Screenshot (200)"
}
],
"product_name": "product test 1"
},
{
"product_type": "Bikes",
"product_imgs": [
{
"id": 7,
"url": "City Bike/banderaa"
},
{
"id": 8,
"url": "City Bike/banderaa"
}
],
"product_name": "product test 2"
}
]
I would like to modify the items inside the array product_imgs of each object , but for that , having in mind this array comes from a request , i do create a readable copy an over that i set the logic.
let instance=[...allProducts];
then using a double for each (though i also tried using a doule for loop) i reach till every image inside the array of objects product_imgs of each object :
instance.forEach(array=>array.product_imgs.map(element => {
this.imgDownLoaderFirebase
.ref(element.url)
.getDownloadURL()
.toPromise()
.then((url) => {
console.log(url);
//then in this space once the url of some firebase endpoint is reached and else
//i would like to modify that object inside the array product_imgs which is at the same time
//part of the instance array.
//For that i expose that this new url gotten would be asigned as the new
//value thus
element = { ...element };
element.url=url
console.log(element);
console.log(instance);//Printing the general array in order to check if changes committed
})
})
I want to specify that i use first a foreach and then a map in order to modify the inner array of objects result , but using a double for each doesn't precisely inmprove this situation:
instance.forEach(array=>array.product_imgs.forEach(element => {........
Then checking the logs , the element (item url) inside the array of objects product_imgs of the array of obejcts instance , is modified , but the external array containing the inner modified not
How could i improve this?
Thanks
If your goal is to extract all product_img values from your array, you could try something like the following :
// This line will convert your array of object into an array of array of urls, using a destructuring process
const urls = allProducts.map(({ product_img }) => product_img);
// This line will merge the previous result into a single-level array of urls that you can iterate onto.
const result = [].concat([], ...res);
Edit : I forgot to mention that this process will in fact return an array of objects including your id and url.

Polymer JS reading from JSON file

I'm trying to read a sub-array from a JSON file in Polymer in JS and return the sub-array to be used in a dom-repeat. However, it tells me that the sub-array is undefined. I tried re-structuring the JSON file in various ways but no luck. I think I'm not using the right syntax somewhere.
Right now the JSON looks like this:
{
"url": "dn8",
"volpage": "DN iii 1",
"languages": [{
"pt": {
"authors": ["Laera"],
"titlelan": "Title in Portuguese"
},
"fr": {
"authors": ["Moi"],
"titlelan": "Title in French"
},
"es": {
"authors": ["Jesus"]
}
}]
}
I'm trying to get a sub-array called languageData which just hold the specific data for an input-language. The input has the correct value for the inputLanguage, like for instance "pt". My JS looks like this:
Polymer({
is: 'test-data',
properties: {
inputUrl: String,
inputLanguage: String,
inputData: {
type: Array,
notify: true,
value: function(){return []}
},
languageData: {
type: Array,
computed: '_computeLanguage(inputData,inputLanguage)'
}
},
_computeLanguage: function(inputData,inputLanguage) {
var lanarray = inputData.languages[inputLanguage];
return lanarray ? lanarray : "";
}
});
Any help is very much appreciated!
As you can see the languages property of your JSON is not an Object but and Array.
Your "Polymer code" works well, the problem is that you are trying to get the languageData as if it were an Array:
var lanarray = inputData.languages[inputLanguage];
Actually, languages contains an array of objects and you can't find your lang object in this way.
A possible solution could be:
var lanarray = inputData.languages[0][inputLanguage];

Using 'this' keyword in JavaScript object

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

Regex for parsing single key: values out of JSON in Javascript

I'm trying to see if it's possible to lookup individual keys out of a JSON string in Javascript and return it's Value with Regex. Sort of like building a JSON search tool.
Imagine the following JSON
"{
"Name": "Humpty",
"Age": "18",
"Siblings" : ["Dracula", "Snow White", "Merlin"],
"Posts": [
{
"Title": "How I fell",
"Comments": [
{
"User":"Fairy God Mother",
"Comment": "Ha, can't say I didn't see it coming"
}
]
}
]
}"
I want to be able to search through the JSON string and only pull out individual properties.
lets assume it's a function already, it would look something like.
function getPropFromJSON(prop, JSONString){
// Obviously this regex will only match Keys that have
// String Values.
var exp = new RegExp("\""+prop+"\"\:[^\,\}]*");
return JSONString.match(exp)[0].replace("\""+prop+"\":","");
}
It would return the substring of the Value for the Key.
e.g.
getPropFromJSON("Comments")
> "[
{
"User":"Fairy God Mother",
"Comment": "Ha, can't say I didn't see it coming"
}
]"
If your wondering why I want to do this instead of using JSON.parse(), I'm building a JSON document store around localStorage. localStorage only supports key/value pairs, so I'm storing a JSON string of the entire Document in a unique Key. I want to be able to run a query on the documents, ideally without the overhead of JSON.parsing() the entire Collection of Documents then recursing over the Keys/nested Keys to find a match.
I'm not the best at regex so I don't know how to do this, or if it's even possible with regex alone. This is only an experiment to find out if it's possible. Any other ideas as a solution would be appreciated.
I would strongly discourage you from doing this. JSON is not a regular language as clearly stated here: https://cstheory.stackexchange.com/questions/3987/is-json-a-regular-language
To quote from the above post:
For example, consider an array of arrays of arrays:
[ [ [ 1, 2], [2, 3] ] , [ [ 3, 4], [ 4, 5] ] ]
Clearly you couldn't parse that with true regular expressions.
I'd recommend converting your JSON to an object (JSON.parse) & implementing a find function to traverse the structure.
Other than that, you can take a look at guts of Douglas Crockford's json2.js parse method. Perhaps an altered version would allow you to search through the JSON string & just return the particular object you were looking for without converting the entire structure to an object. This is only useful if you never retrieve any other data from your JSON. If you do, you might as well have converted the whole thing to begin with.
EDIT
Just to further show how Regex breaks down, here's a regex that attempts to parse JSON
If you plug it into http://regexpal.com/ with "Dot Matches All" checked. You'll find that it can match some elements nicely like:
Regex
"Comments"[ :]+((?=\[)\[[^]]*\]|(?=\{)\{[^\}]*\}|\"[^"]*\")
JSON Matched
"Comments": [
{
"User":"Fairy God Mother",
"Comment": "Ha, can't say I didn't see it coming"
}
]
Regex
"Name"[ :]+((?=\[)\[[^]]*\]|(?=\{)\{[^\}]*\}|\"[^"]*\")
JSON Matched
"Name": "Humpty"
However as soon as you start querying for the higher structures like "Posts", which has nested arrays, you'll find that you cannot correctly return the structure since the regex does not have context of which "]" is the designated end of the structure.
Regex
"Posts"[ :]+((?=\[)\[[^]]*\]|(?=\{)\{[^\}]*\}|\"[^"]*\")
JSON Matched
"Posts": [
{
"Title": "How I fell",
"Comments": [
{
"User":"Fairy God Mother",
"Comment": "Ha, can't say I didn't see it coming"
}
]
\{|\}|\[|\]|,|:|(\\-)?\\d+(\\.\\d+)?|".+?"
You can use the following regex and iterate with a match over all tokens of a json. You can tokenize the JSON, but the parsing part has to be implemented by you.
Since you're using JavaScript as I assume from the tags, your best way to encode the JSON stays JSON.parse().
I'm almost 10 years late to the party, but I came up with this.
Not tested in crazier JSONs than this, but it solves my use cases.
const obj1 = {
id: 1,
'name.1': '123',
address: {
'address.1': 'Chicken Dinner Road, 69',
'address.2': 'Psycho lane, 666',
},
'age.1': {
'thisIsSomeCrazyJson.3': 10,
age: 50,
},
types: [
{
id: 22,
'name.name': '123',
typeOption: {
id: 1,
'whoTFWroteThisJSON.2': '123',
},
},
{
id: 32,
'name.1': '123',
},
],
};
const obj2 = {
Name: 'Humpty',
Age: '18',
Siblings: ['Dracula', 'Snow White', 'Merlin'],
Posts: [
{
Title: 'How I fell',
Comments: [
{
'User': 'Fairy God Mother',
'Comment': "Ha, can't say I didn't see it coming",
},
],
},
],
};
function matchKeyDeep(input, pattern) {
return Object.entries(input).reduce((nextInput, [key, value]) => {
const isMatch = pattern.test(key);
if (Array.isArray(value)) {
const arrValue = value;
let nextValue = arrValue.map((arrItem) => {
if (typeof arrItem === 'object') {
return matchKeyDeep(arrItem, pattern);
}
return arrItem;
});
if (!isMatch && Array.isArray(nextValue)) {
nextValue = nextValue.filter((v) => (typeof v === 'object' && v !== null));
if (nextValue.length === 0) return nextInput;
}
nextInput[key] = nextValue;
return nextInput;
}
if (typeof value === 'object') {
const recurse = matchKeyDeep(value, pattern);
if (!isMatch && Object.keys(recurse).length === 0) {
return nextInput;
}
nextInput[key] = recurse;
return nextInput;
}
if (isMatch) {
nextInput[key] = value;
}
return nextInput;
}, {});
}
const res = matchKeyDeep(obj1, /\.\d/);
const res2 = matchKeyDeep(obj2, /Comment/);
console.log(res);
console.log(res2);
First, stringify the JSON object. Then, you need to store the starts and lengths of the matched substrings. For example:
"matched".search("ch") // yields 3
For a JSON string, this works exactly the same (unless you are searching explicitly for commas and curly brackets in which case I'd recommend some prior transform of your JSON object before performing regex (i.e. think :, {, }).
Next, you need to reconstruct the JSON object. The algorithm I authored does this by detecting JSON syntax by recursively going backwards from the match index. For instance, the pseudo code might look as follows:
find the next key preceding the match index, call this theKey
then find the number of all occurrences of this key preceding theKey, call this theNumber
using the number of occurrences of all keys with same name as theKey up to position of theKey, traverse the object until keys named theKey has been discovered theNumber times
return this object called parentChain
With this information, it is possible to use regex to filter a JSON object to return the key, the value, and the parent object chain.
You can see the library and code I authored at http://json.spiritway.co/

Categories

Resources