Make a Person Using Getters and Setters: JavaScript not understanding where my codebase is performing the wrong action. Help requested - constructor

The "Make a Person" intermediate algorithm scripting challenge on freeCodeCamp requires you to fill an object constructor with the following methods:
/*
getFirstName()
getLastName()
getFullName()
setFirstName(first)
setLastName(last)
setFullName(firstAndLast)
*/
My code is as follows and includes the test cases and their required values commented in at the end:
const Person = function(firstAndLast) {
// Only change code below this line
// Complete the method below and implement the others similarly
//create a holder variable to hold a copy of the full name passed as parameter
let fullName = firstAndLast;
//create a variable to pay respect to "DRY" so as not to have to type this variable and method multiple times throughout
let splitter = fullName.split(" ");
//return the first name from the full name passed as parameter
this.getFirstName = function() {
return splitter[0];
};
//return the last name from the full name passed as parameter
this.getLastName = function() {
return splitter[1];
};
//return the full name passed as a parameter
this.getFullName = function() {
return fullName;
};
//update the full name to now include the given first name instead of the original passed parameter
this.setFirstName = function(first) {
fullName = first + " " + splitter[1];
};
//update the full name to now include the given last name instead of the original passed parameter
this.setLastName = function(last) {
fullName = splitter[0] + " " + last;
};
//update the full name to the given firstAndLast name instead of the original passed parameter
this.setFullName = function(newFull) {
fullName = newFull;
};
};
//create a new Person, bob, and name him 'Bob Ross'
const bob = new Person('Bob Ross');
//expected to return => 'Bob Ross'
let result = bob.getFullName();
//no expected return value, but fullName should now return => 'Haskell Curry'
bob.setFullName('Haskell Curry')
//my code here returns => 'Haskell Curry'
let result2 = bob.getFullName();
//my code here returns => 'Bob'
//should be returning => 'Haskell'
let result3 = bob.getFirstName();
//my code here returns => 'Ross'
//should be returning => 'Curry'
let result4 = bob.getLastName();
//Console.log in place for value testing during algorithm creation
console.log(result, result2, result3, result4)
//Check for length of bob, should not exceed 6 for the purposes of this test
console.log(Object.keys(bob).length)
/*Tests
Required returning values for each test
bob instanceof Person => true (Passing)
Object.keys(bob).length => 6 (Passing)
bob.firstName => undefined (Passing)
bob.lastName => undefined (Passing)
bob.getFirstName() => "Bob" (Passing)
bob.getLastName() => "Ross" (Passing)
bob.getFullName() => "Bob Ross" (Passing)
bob.getFullName() => "Haskell Ross" AFTER bob.setFirstName("Haskell") (Passing)
bob.getFullName() => "Haskell Curry" AFTER bob.setLastName("Curry") (Passing)
bob.getFullName() => "Haskell Curry" AFTER bob.setFullName("Haskell Curry") (Passing)
bob.getFirstName() => "Haskell" AFTER bob.setFullName("Haskell Curry") (NOT Passing)
bob.getLastName() => "Curry" AFTER bob.setFullName("Haskell Curry") (NOT Passing)
*/
After checking my code up against the solution code, the two are virtually the same, the only differences are the usage of
let splitter = fullName.split(" ")
//this does not exist in the solution code
//used in my code to avoid having to type fullName.split(" ") multiple times throughout
And where the setters ask for parameters "first", "last", and "newFull", respectively, the solution code uses "name" for each instead
I couldn't imagine that these two differences could make that big of a difference, so could I get some clarity in the understanding of their importance, and furthermore, why my code won't pass all the cases as is? Thanks in advance!

Related

With library Cheerio.js I'd like to replace the content with a some string rather than leaving it

I've written a content scraper code with library Cheerio.js, it read the content of a webpage and grabs me some values like location name, street address, and phone no.
In some places, the street address isn't provided so it skips that place which isn't cool.
Here is an example of the webpage that I'm getting the data from.
https://www.yellowpages.com/search?search_terms=Medical%20Ambulance&geo_location_terms=Los%20Angeles%2C%20CA&page=2
How do I detect if the street address isn't located here & my code should replace the empty content with something else?
Full code is located here.
https://github.com/jpca999/yellowpageScrapper/blob/master/indexWithAsyncArray.js
Currently, in the output, it just skips the empty street names.
const getStreetAddress = async () => {
console.log(' calling getStreetAddress');
const html = await rp(baseURL + searchURL);
const businessMap = cheerio('div.street-address', html).map(async (i, e) => {
const streetAddress = e.children[0].parent.children[0].data;
console.log('Here it shold detect if the variable "streetAddress" has some value then leave it or else replace the value with something' );
return {
streetAddress,
}
})
.get();
return Promise.all(businessMap);
};
Here's the output.
If this were mine, and unfortunately I have no way to test this myself with your entire code base, I would just check the value against a default value. The basic paradigm I've used in JavaScript, which some people like and some don't, and you can adapt to your own taste, is
const actualValue = possibleValue || defaultValue;
So in your case it might be
const streetAddress = e.children[0].parent.children[0].data || 'No Street Address Provided';
This is because null, undefined, and empty string are all falsey value and such will cause the default value to be assigned.
You can do the same thing after your object is returned, for example
const businessMap = ... blah ... .get();
businessMap.streetAddress = businessMap.streetAddress || 'No Street Address Provided';
return Promise.all(businessMap);
That page doesn't load for me, but it should look something like (assuming the css for streetAddress is .foo)
const $ = cheerio.load(html)
const businessMap = $('div.street-address').get().map(div => {
return {
streetAddress: $(div).find('.foo').first().text() || "???"
}
})
Also note that cheerio doesn't use promises because there's no IO.
Also note that the empty string "" is falsey in JS which is why this works.

Mapping over a const variable and returning to the value of an input [duplicate]

I'm trying to access a property of an object using a dynamic name. Is this possible?
const something = { bar: "Foobar!" };
const foo = 'bar';
something.foo; // The idea is to access something.bar, getting "Foobar!"
There are two ways to access properties of an object:
Dot notation: something.bar
Bracket notation: something['bar']
The value between the brackets can be any expression. Therefore, if the property name is stored in a variable, you have to use bracket notation:
var something = {
bar: 'foo'
};
var foo = 'bar';
// both x = something[foo] and something[foo] = x work as expected
console.log(something[foo]);
console.log(something.bar)
This is my solution:
function resolve(path, obj) {
return path.split('.').reduce(function(prev, curr) {
return prev ? prev[curr] : null
}, obj || self)
}
Usage examples:
resolve("document.body.style.width")
// or
resolve("style.width", document.body)
// or even use array indexes
// (someObject has been defined in the question)
resolve("part.0.size", someObject)
// returns null when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})
In javascript we can access with:
dot notation - foo.bar
square brackets - foo[someVar] or foo["string"]
But only second case allows to access properties dynamically:
var foo = { pName1 : 1, pName2 : [1, {foo : bar }, 3] , ...}
var name = "pName"
var num = 1;
foo[name + num]; // 1
// --
var a = 2;
var b = 1;
var c = "foo";
foo[name + a][b][c]; // bar
Following is an ES6 example of how you can access the property of an object using a property name that has been dynamically generated by concatenating two strings.
var suffix = " name";
var person = {
["first" + suffix]: "Nicholas",
["last" + suffix]: "Zakas"
};
console.log(person["first name"]); // "Nicholas"
console.log(person["last name"]); // "Zakas"
This is called computed property names
You can achieve this in quite a few different ways.
let foo = {
bar: 'Hello World'
};
foo.bar;
foo['bar'];
The bracket notation is specially powerful as it let's you access a property based on a variable:
let foo = {
bar: 'Hello World'
};
let prop = 'bar';
foo[prop];
This can be extended to looping over every property of an object. This can be seem redundant due to newer JavaScript constructs such as for ... of ..., but helps illustrate a use case:
let foo = {
bar: 'Hello World',
baz: 'How are you doing?',
last: 'Quite alright'
};
for (let prop in foo.getOwnPropertyNames()) {
console.log(foo[prop]);
}
Both dot and bracket notation also work as expected for nested objects:
let foo = {
bar: {
baz: 'Hello World'
}
};
foo.bar.baz;
foo['bar']['baz'];
foo.bar['baz'];
foo['bar'].baz;
Object destructuring
We could also consider object destructuring as a means to access a property in an object, but as follows:
let foo = {
bar: 'Hello World',
baz: 'How are you doing?',
last: 'Quite alright'
};
let prop = 'last';
let { bar, baz, [prop]: customName } = foo;
// bar = 'Hello World'
// baz = 'How are you doing?'
// customName = 'Quite alright'
You can do it like this using Lodash get
_.get(object, 'a[0].b.c');
UPDATED
Accessing root properties in an object is easily achieved with obj[variable], but getting nested complicates things. Not to write already written code I suggest to use lodash.get.
Example
// Accessing root property
var rootProp = 'rootPropert';
_.get(object, rootProp, defaultValue);
// Accessing nested property
var listOfNestedProperties = [var1, var2];
_.get(object, listOfNestedProperties);
Lodash get can be used in different ways, the documentation lodash.get
To access a property dynamically, simply use square brackets [] as follows:
const something = { bar: "Foobar!" };
const userInput = 'bar';
console.log(something[userInput])
The problem
There's a major gotchya in that solution! (I'm surprised other answers have not brought this up yet). Often you only want to access properties that you've put onto that object yourself, you don't want to grab inherited properties.
Here's an illustration of this issue. Here we have an innocent-looking program, but it has a subtle bug - can you spot it?
const agesOfUsers = { sam: 16, sally: 22 }
const username = prompt('Enter a username:')
if (agesOfUsers[username] !== undefined) {
console.log(`${username} is ${agesOfUsers[username]} years old`)
} else {
console.log(`${username} is not found`)
}
When prompted for a username, if you supply "toString" as a username, it'll give you the following message: "toString is function toString() { [native code] } years old". The issue is that agesOfUsers is an object, and as such, automatically inherits certain properties like .toString() from the base Object class. You can look here for a full list of properties that all objects inherit.
Solutions
Use a Map data structure instead. The stored contents of a map don't suffer from prototype issues, so they provide a clean solution to this problem.
const agesOfUsers = new Map()
agesOfUsers.set('sam', 16)
agesOfUsers.set('sally', 2)
console.log(agesOfUsers.get('sam')) // 16
Use an object with a null prototype, instead of the default prototype. You can use Object.create(null) to create such an object. This sort of object does not suffer from these prototype issues, because you've explicitly created it in a way that it does not inherit anything.
const agesOfUsers = Object.create(null)
agesOfUsers.sam = 16
agesOfUsers.sally = 22;
console.log(agesOfUsers['sam']) // 16
console.log(agesOfUsers['toString']) // undefined - toString was not inherited
You can use Object.hasOwn(yourObj, attrName) to first check if the dynamic key you wish to access is directly on the object and not inherited (learn more here). This is a relatively newer feature, so check the compatibility tables before dropping it into your code. Before Object.hasOwn(yourObj, attrName) came around, you would achieve this same effect via Object.prototype.hasOwnProperty.call(yourObj, attrName). Sometimes, you might see code using yourObj.hasOwnProperty(attrName) too, which sometimes works but it has some pitfalls that you can read about here.
// Try entering the property name "toString",
// you'll see it gets handled correctly.
const user = { name: 'sam', age: 16 }
const propName = prompt('Enter a property name:')
if (Object.hasOwn(user, propName)) {
console.log(`${propName} = ${user[propName]}`)
} else {
console.log(`${propName} is not found`)
}
If you know the key you're trying to use will never be the name of an inherited property (e.g. maybe they're numbers, or they all have the same prefix, etc), you can choose to use the original solution.
I came across a case where I thought I wanted to pass the "address" of an object property as data to another function and populate the object (with AJAX), do lookup from address array, and display in that other function. I couldn't use dot notation without doing string acrobatics so I thought an array might be nice to pass instead. I ended-up doing something different anyway, but seemed related to this post.
Here's a sample of a language file object like the one I wanted data from:
const locs = {
"audioPlayer": {
"controls": {
"start": "start",
"stop": "stop"
},
"heading": "Use controls to start and stop audio."
}
}
I wanted to be able to pass an array such as: ["audioPlayer", "controls", "stop"] to access the language text, "stop" in this case.
I created this little function that looks-up the "least specific" (first) address parameter, and reassigns the returned object to itself. Then it is ready to look-up the next-most-specific address parameter if one exists.
function getText(selectionArray, obj) {
selectionArray.forEach(key => {
obj = obj[key];
});
return obj;
}
usage:
/* returns 'stop' */
console.log(getText(["audioPlayer", "controls", "stop"], locs));
/* returns 'use controls to start and stop audio.' */
console.log(getText(["audioPlayer", "heading"], locs));
ES5 // Check Deeply Nested Variables
This simple piece of code can check for deeply nested variable / value existence without having to check each variable along the way...
var getValue = function( s, context ){
return Function.call( context || null, 'return ' + s )();
}
Ex. - a deeply nested array of objects:
a = [
{
b : [
{
a : 1,
b : [
{
c : 1,
d : 2 // we want to check for this
}
]
}
]
}
]
Instead of :
if(a && a[0] && a[0].b && a[0].b[0] && a[0].b[0].b && a[0].b[0].b[0] && a[0].b[0].b[0].d && a[0].b[0].b[0].d == 2 ) // true
We can now :
if( getValue('a[0].b[0].b[0].d') == 2 ) // true
Cheers!
Others have already mentioned 'dot' and 'square' syntaxes so I want to cover accessing functions and sending parameters in a similar fashion.
Code jsfiddle
var obj = {method:function(p1,p2,p3){console.log("method:",arguments)}}
var str = "method('p1', 'p2', 'p3');"
var match = str.match(/^\s*(\S+)\((.*)\);\s*$/);
var func = match[1]
var parameters = match[2].split(',');
for(var i = 0; i < parameters.length; ++i) {
// clean up param begninning
parameters[i] = parameters[i].replace(/^\s*['"]?/,'');
// clean up param end
parameters[i] = parameters[i].replace(/['"]?\s*$/,'');
}
obj[func](parameters); // sends parameters as array
obj[func].apply(this, parameters); // sends parameters as individual values
I asked a question that kinda duplicated on this topic a while back, and after excessive research, and seeing a lot of information missing that should be here, I feel I have something valuable to add to this older post.
Firstly I want to address that there are several ways to obtain the value of a property and store it in a dynamic Variable. The first most popular, and easiest way IMHO would be:
let properyValue = element.style['enter-a-property'];
however I rarely go this route because it doesn't work on property values assigned via style-sheets. To give you an example, I'll demonstrate with a bit of pseudo code.
let elem = document.getElementById('someDiv');
let cssProp = elem.style['width'];
Using the code example above; if the width property of the div element that was stored in the 'elem' variable was styled in a CSS style-sheet, and not styled inside of its HTML tag, you are without a doubt going to get a return value of undefined stored inside of the cssProp variable. The undefined value occurs because in-order to get the correct value, the code written inside a CSS Style-Sheet needs to be computed in-order to get the value, therefore; you must use a method that will compute the value of the property who's value lies within the style-sheet.
Henceforth the getComputedStyle() method!
function getCssProp(){
let ele = document.getElementById("test");
let cssProp = window.getComputedStyle(ele,null).getPropertyValue("width");
}
W3Schools getComputedValue Doc This gives a good example, and lets you play with it, however, this link Mozilla CSS getComputedValue doc talks about the getComputedValue function in detail, and should be read by any aspiring developer who isn't totally clear on this subject.
As a side note, the getComputedValue method only gets, it does not set. This, obviously is a major downside, however there is a method that gets from CSS style-sheets, as well as sets values, though it is not standard Javascript.
The JQuery method...
$(selector).css(property,value)
...does get, and does set. It is what I use, the only downside is you got to know JQuery, but this is honestly one of the very many good reasons that every Javascript Developer should learn JQuery, it just makes life easy, and offers methods, like this one, which is not available with standard Javascript.
Hope this helps someone!!!
For anyone looking to set the value of a nested variable, here is how to do it:
const _ = require('lodash'); //import lodash module
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4
Documentation: https://lodash.com/docs/4.17.15#set
Also, documentation if you want to get a value: https://lodash.com/docs/4.17.15#get
You can do dynamically access the property of an object using the bracket notation. This would look like this obj[yourKey] however JavaScript objects are really not designed to dynamically updated or read. They are intended to be defined on initialisation.
In case you want to dynamically assign and access key value pairs you should use a map instead.
const yourKey = 'yourKey';
// initialise it with the value
const map1 = new Map([
['yourKey', 'yourValue']
]);
// initialise empty then dynamically assign
const map2 = new Map();
map2.set(yourKey, 'yourValue');
console.log(map1.get(yourKey));
console.log(map2.get(yourKey));
demo object example
let obj = {
name: {
first_name: "Bugs",
last_name: "Founder",
role: "Programmer"
}
}
dotted string key for getting the value of
let key = "name.first_name"
Function
const getValueByDottedKeys = (obj, strKey)=>{
let keys = strKey.split(".")
let value = obj[keys[0]];
for(let i=1;i<keys.length;i++){
value = value[keys[i]]
}
return value
}
Calling getValueByDottedKeys function
value = getValueByDottedKeys(obj, key)
console.log(value)
output
Bugs
const getValueByDottedKeys = (obj, strKey)=>{
let keys = strKey.split(".")
let value = obj[keys[0]];
for(let i=1;i<keys.length;i++){
value = value[keys[i]]
}
return value
}
let obj = {
name: {
first_name: "Bugs",
last_name: "Founder",
role: "Programmer"
}
}
let key = "name.first_name"
value = getValueByDottedKeys(obj, key)
console.log(value)
I bumped into the same problem, but the lodash module is limited when handling nested properties. I wrote a more general solution following the idea of a recursive descendent parser. This solution is available in the following Gist:
Recursive descent object dereferencing
Finding Object by reference without, strings,
Note make sure the object you pass in is cloned , i use cloneDeep from lodash for that
if object looks like
const obj = {data: ['an Object',{person: {name: {first:'nick', last:'gray'} }]
path looks like
const objectPath = ['data',1,'person',name','last']
then call below method and it will return the sub object by path given
const child = findObjectByPath(obj, objectPath)
alert( child) // alerts "last"
const findObjectByPath = (objectIn: any, path: any[]) => {
let obj = objectIn
for (let i = 0; i <= path.length - 1; i++) {
const item = path[i]
// keep going up to the next parent
obj = obj[item] // this is by reference
}
return obj
}
You can use getter in Javascript
getter Docs
Check inside the Object whether the property in question exists,
If it does not exist, take it from the window
const something = {
get: (n) => this.n || something.n || window[n]
};
You should use JSON.parse, take a look at https://www.w3schools.com/js/js_json_parse.asp
const obj = JSON.parse('{ "name":"John", "age":30, "city":"New York"}')
console.log(obj.name)
console.log(obj.age)

Error while saving JSON data to Firestore collection using cloud function

I am trying to insert array in my firebase collection from cloud function. I need to have multiple lines in one document so for each line i am inserting an array. Please check my attached screenshot where you can see line0 , same way i need to have Line1,Line2,Line3..,Line n in the same document.
for line0 i am passing array from code like below and its working fine.
admin.firestore().collection("qbContestWinners").add(
{
'cmpientryid': context.params.processId,
'qbid': '',
'qbsyncdate': '',
'qbsyncstatus': 'pending',
'Line0':
{
id: "0",
description: 'PRIZE AMOUNT',
amount: 1000,
accountrefid: contestresultData.qbcontestid,
accountrefname: contestresultData.qbcontestname,
contestresultId: context.params.processId,
},
})
when i am looping through data i am getting from another table , i am not able to generate proper JSON to insert.
below is how i am looping and creating JSON after getting data from another table.
i = 1;
admin.firestore().collection("results").where('cid', '==', 'LKRRk2XXXXXXXX')
.orderBy("rank", "asc").get().then(snapshots =>
{
snapshots.forEach(doc =>
{
const contestresultId = doc.id;
const prizeAmount = doc.data().prizeamt;
const userId = doc.data().userid;
const lineNum = "Line" + i;
console.log("new line numner is: ", lineNum);
console.log(`lineNum? ${lineNum}`);
const linetxt = "Line" + String(i);
const insertData = "{"+linetxt +
":{id:'" + i +
"', description: 'PRIZE AMOUNT'"+
", amount:" + prizeAmount + "," +
"accountrefid:"+ contestresultData.qbcontestid +","+
"accountrefname:'" +contestresultData.qbcontestname +"',"+
"contestresultId:'" + contestresultId +"'," +
"},}"
const finalInsert = JSON.stringify(insertData);
const finalJSON = JSON.parse(finalInsert);
admin.firestore().collection("qbContestWinners").doc(mainID).set(
finalInsert.toJSON(),
{
merge: true
});
i= i+1;
});
});
using this code i am getting error
finalInsert.toJSON is not a function
Actually, the Line0 field is a map and not an Array, see this doc for more details.
So, if you want to create similar fields (Line1, Line2, ...), you simply need to pass a JavaScript Object to the set() method, as follows:
snapshots.forEach(doc => {
const contestresultId = doc.id;
const prizeAmount = doc.data().prizeamt;
const userId = doc.data().userid;
const lineNum = "Line" + i;
console.log("new line numner is: ", lineNum);
console.log(`lineNum? ${lineNum}`);
const lineObj = {
id: i,
description: 'PRIZE AMOUNT',
accountrefid: contestresultData.qbcontestid, //Not sure if you have defined contestresultData somewhere...
//...
}
const dataObj = {};
dataObj["Line" + i] = lineObj // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors
admin.firestore().collection("qbContestWinners").doc(mainID).set(dataObj, {merge: true});
i= i+1;
});
HOWEVER, note that you must return a promise that resolves when all the asynchronous work in your Cloud Function is complete (i.e. call to the Firestore set() method).
This is explained in the official Firebase video series, watch in particular the three videos titled "Learn JavaScript Promises".
Since you are calling several times the set() method in a forEach loop, you need to use Promise.all() in order to return a Promise when all these parallel calls to the set() method are completed.
The following should do the trick:
let i = 1;
return admin.firestore().collection("results") // <-- See the return here
.where('cid', '==', 'LKRRk2XXXXXXXX')
.orderBy("rank", "asc").get()
.then(snapshots => {
const promises = [];
snapshots.forEach(doc => {
const contestresultId = doc.id;
const prizeAmount = doc.data().prizeamt;
const userId = doc.data().userid;
const lineNum = "Line" + i;
const lineObj = {
id: i,
description: 'PRIZE AMOUNT',
accountrefid: contestresultData.qbcontestid,
//...
}
const dataObj = {};
dataObj[lineNum] = lineObj;
promises.push(admin.firestore().collection("qbContestWinners").doc(mainID).set(dataObj, {merge: true}));
i= i+1;
});
return Promise.all(promises) // <-- See the return here
});
A last remark: if mainID keeps the same value in the snapshots.forEach loop, you may adopt a totally different approach, consisting in building a JavaScript object with several LineXX properties and call the set() method only once. Since you didn't share the entire code of your Cloud Function it is impossible to say if this approach should be used or not.
first to the error
You stringify and parse a string. The problem here seems to be the order. You have to parse a "String" and to stringify an "Object". The result won't have a toJSON Method as well, but u can just stringify the Object to get a json.
the second thing
Why do you use a string to create your object? You shouldn't. Just use an object.
the third thing
You should not use Objects as Arrays. Not even in firebase.
Just use arrays. Example:
[Line0Object, Line1Object, ...]
Hint: If your array can work as its own collection. Just use a SubCollection. This might fit your needs.

How to alter keys in immutable map?

I've a data structure like this (generated by normalizr):
const data = fromJS({
templates: {
"83E51B08-5F55-4FA2-A2A0-99744AE7AAD3":
{"uuid": "83E51B08-5F55-4FA2-A2A0-99744AE7AAD3", test: "bla"},
"F16FB07B-EF7C-440C-9C21-F331FCA93439":
{"uuid": "F16FB07B-EF7C-440C-9C21-F331FCA93439", test: "bla"}
}
})
Now I try to figure out how to replace the UUIDs in both the key and the value of the template entries. Basically how can I archive the following output:
const data = fromJS({
templates: {
"DBB0B4B0-565A-4066-88D3-3284803E0FD2":
{"uuid": "DBB0B4B0-565A-4066-88D3-3284803E0FD2", test: "bla"},
"D44FA349-048E-4006-A545-DBF49B1FA5AF":
{"uuid": "D44FA349-048E-4006-A545-DBF49B1FA5AF", test: "bla"}
}
})
A good candidate seems to me the .mapEntries() method, but I'm struggling on how to use it ...
// this don't work ... :-(
const result = data.mapEntries((k, v) => {
const newUUID = uuid.v4()
return (newUUID, v.set('uuid', newUUID))
})
Maybe someone can give me a hand here?
mapEntries is the correct method. From the documentation, the mapping function has the following signature:
mapper: (entry: [K, V], index: number, iter: this) => [KM, VM]
This means that the first argument is the entry passed in as an array of [key, value]. Similarly, the return value of the mapper function should be an array of the new key and the new value. So your mapper function needs to look like this:
([k, v]) => {
const newUUID = uuid.v4()
return [newUUID, v.set('uuid', newUUID)]
}
This is equivalent to the following (more explicit) function:
(entry) => {
const key = entry[0]; // note that key isn't actually used, so this isn't necessary
const value = entry[1];
const newUUID = uuid.v4()
return [newUUID, value.set('uuid', newUUID)]
}
One thing to note is that the templates are nested under the templates property, so you can't map data directly -- instead you'll want to use the update function.
data.update('templates', templates => template.mapEntries(...)))
So putting everything together, your solution should look like the following:
const result = data.update('templates', templates =>
templates.mapEntries(([k, v]) => {
const newUUID = uuid.v4()
return [newUUID, v.set('uuid', newUUID)]
})
);

Conditional Anonymous type

I am working on Web API and using Anonymous type to make JSON as output. I am stuck in the following scenario:
If there is no record(VALUE) available then i don't want to show that KEY. Meaning, Key should only appear when and only when there is value.
Below is the JSON object i am creating -
"TU": [
{
"BLOCK": [
[
"00:00",
"00:59"
]
]
}
],
"WE": [],// empty
"TH": [],// empty
"FR": [],// empty
"SA": [] // empty
Here for Tuesday we do have records and hence its showing but later for WE,TH,FR,SA there are not records and hence i don't want to show them so my result will be MO/TU only.
I am using below code:
var result = new
{
CustomerID = custId,
DeviceID = dId,
Kind = kind,
WebList = filter.Select(filt => new
{
URL = filt.FilterName,
TimeBlockFlag = new ChicoHelper().GetFlag(browserlimit, filt.ID, filt.FilterOptionID, KindId),
DAILY = browserlimit.Where(xx => xx.FilterID == filt.ID && xx.OptionTypeID == daily).Select(xx => xx.BlockTimeLimit).SingleOrDefault(),
WEEKLY = browserlimit.Where(xx => xx.FilterID == filt.ID && xx.OptionTypeID == weekly).Select(xx => xx.BlockTimeLimit).SingleOrDefault(),
MONTHLY = browserlimit.Where(xx => xx.FilterID == filt.ID && xx.OptionTypeID == monthly).Select(xx => xx.BlockTimeLimit).SingleOrDefault(),
HASVALUES = browserlimit.Where(xx => xx.FilterID == filt.ID).Count() > 0 ? 1 : 0,
BLOCKTYPE = new ChicoHelper().GetBlockType(browserlimit,filt.ID,filt.FilterOptionID,KindId),
SU = blockedlimit.Where(x => x.OptionID == sunday && x.FilterID == filt.ID).GroupBy(x => new { x.BlockDay })
.Select(x => new
{
BLOCK = x.Select(y =>
new[] { y.BlockStartTime.MakeFormatedTime(), y.BlockEndTime.MakeFormatedTime() }
)
}),
MO = blockedlimit.Where(x => x.OptionID == monday && x.FilterID == filt.ID).GroupBy(x => new { x.BlockDay })
.Select(x => new
{
BLOCK = x.Select(y =>
new[] { y.BlockStartTime.MakeFormatedTime(), y.BlockEndTime.MakeFormatedTime() }
)
}),
TU = blockedlimit.Where(x => x.OptionID == tuesday && x.FilterID == filt.ID).GroupBy(x => new { x.BlockDay })
.Select(x => new
{
BLOCK = x.Select(y =>
new[] { y.BlockStartTime.MakeFormatedTime(), y.BlockEndTime.MakeFormatedTime() }
)
}),
// if i can put some condition like if there is not record for WE then don't show it.
WE = blockedlimit.Where(x => x.OptionID == wednesday && x.FilterID == filt.ID).GroupBy(x => new { x.BlockDay })
.Select(x => new
{
BLOCK = x.Select(y =>
new[] { y.BlockStartTime.MakeFormatedTime(), y.BlockEndTime.MakeFormatedTime() }
)
}),
The main reason for doing this is to reduce the JSON size which will be consumed by Mobile Devices.
Please help me with this.
The properties of an anonymous type are fixed at compile-time - you can't make them conditional. However, some other approaches you might want to think about:
You could investigate whether a property is still included in the JSON representation if its value is null. If it's not, you could add an extension method NullIfEmpty() which returns null if its input is empty.
You could try performing the JSON conversion from the anonymous type in code first, then delete any properties with an empty set of results, then just return that JSON object from the API. (I don't know Web API myself, but there must be a way of saying "Here's a JSON object - ask it for its string representation" rather than using an anonymous type.)
You could ditch the anonymous type entirely, and build up the JSON representation programmatically, setting just the properties you want.
In any approach, I would strongly advise you to extract a common method to come up with the property value based on a day of the week, so you can have:
...
SU = blockedLimit.GetDayBlocks(sunday),
MO = blockedLimit.GetDayBlocks(monday),
TU = blockedLimit.GetDayBlocks(tuesday),
...
There's no reason to have all that code repeated 7 times. In fact, I'd probably refactor that part before doing anything else - it'll make it easier to experiment.