Intro
I'm currently using the UPS Rate API to create a shipping plugin for a client to get an estimate on shipping charges for customers during checkout (among other things).
I've briefly used Nodejs in the past, however this would be my first time using it in a production environment, and I want to ensure I'm using best practices for this application.
Code
Below is the request I must send to UPS' API endpoint to get a shipping estimate:
{
"UPSSecurity":{
"UsernameToken":{
"Username":"Your User Id",
"Password":"Your Password"
},
"ServiceAccessToken":{
"AccessLicenseNumber":"Your Access License"
}
},
"RateRequest":{
"Request":{
"RequestOption":"Rate",
"TransactionReference":{
"CustomerContext":"Your Customer Context"
}
},
"Shipment":{
"Shipper":{
"Name":"Shipper Name",
"ShipperNumber":"Shipper Number",
"Address":{
"AddressLine":[
"Address Line ",
"Address Line ",
"Address Line "
],
"City":"City",
"StateProvinceCode":"State Province Code",
"PostalCode":"Postal Code",
"CountryCode":"US"
}
},
"ShipTo":{
"Name":"Ship To Name",
"Address":{
"AddressLine":[
"Address Line ",
"Address Line ",
"Address Line "
],
"City":"City",
"StateProvinceCode":"State Province Code",
"PostalCode":"Postal Code",
"CountryCode":"US"
}
},
"ShipFrom":{
"Name":"Ship From Name",
"Address":{
"AddressLine":[
"Address Line ",
"Address Line ",
"Address Line "
],
"City":"City",
"StateProvinceCode":"State Province Code",
"PostalCode":"Postal Code",
"CountryCode":"US"
}
},
"Service":{
"Code":"03",
"Description":"Service Code Description"
},
"Package":{
"PackagingType":{
"Code":"02",
"Description":"Rate"
},
"Dimensions":{
"UnitOfMeasurement":{
"Code":"IN",
"Description":"inches"
},
"Length":"5",
"Width":"4",
"Height":"3"
},
"PackageWeight":{
"UnitOfMeasurement":{
"Code":"Lbs",
"Description":"pounds"
},
"Weight":"1"
}
},
"ShipmentRatingOptions":{
"NegotiatedRatesIndicator":""
}
}
}
}
Seeing the amount of fields to fill out, whats the best way to approach this, while adhering to basic software engineering principles of low coupling and high cohesion?
Should I do something similar to the code example below, but for each field section?
const shipToAddr1 = "A street with number"
const shipToAddr2 = "Line 2 with number"
const shipToAddr3 = "The third line"
const shipToCity = "Boston"
const shipToStateProvinceCode = "12"
const shipToPostalCode = "01970"
const shipToCountryCode = "US"
const shipToName = "Bob Wallace"
const packageLength = "10"
const packageWidth = "5"
const packageHeight = "18"
const PackageWeight = "12"
//See above code snippet
var jsonRequest = {...}
function writeShipToContents(json, shipToName, shipToAddr1, shipToAddr2,
shipToAddr3){
json.RateRequest.Shipment.ShipTo.Name = shipToName
json.RateRequest.Shipment.ShipTo.Address.AddressLine = [
shipToAddr1,
shipToAddr2,
shipToAddr3
]
}
function writeShipFromContents(json){
...
}
function writePackageDetails(json){
...
}
function writeShipmentRequest(json){
writeShipToContents(json)
writeShipFromContents(json)
writePackageDetails(json)
...
return json
}
writeShipmentRequest(jsonRequest)
My instinct is that many things are wrong with the above code, for instance having each function change the referenced object instead of returning a new object with the populated contents; having the functions use global variables to populate the information; and all in all, this seems like a lot of code for a simple task.
The application will take a POST request with the information as const in the example, then return the results of the shipping estimate.
Should I be creating a dictionary of each field, pass the json and the dictionary contents, and have the function lookup the dictionary items, populate the json, and return the results?
Don't overthink this with unnecessary programming paradigms. According to your comment this is a simple creation of an object whose structure never changes. Treat it as such.
If your task is to create a Javascript Object from values and send it in a POST request, simply create a Javascript Object with the short notation:
const upsPostBody = {
property: value
}
Do this for the whole object, e.g. (excerpt):
const username = 'Your User Id';
const password = 'Your Password';
const accessLicenseNumber: 'Your Access License';
const upsPostBody = {
UPSSecurity:{
UsernameToken: {
Username: username,
Password: password
},
ServiceAccessToken: {
AccessLicenseNumber: accessLicenseNumber
}
}
// Continue for all other keys and values
}
After assigning the values to the object, pass the object as the body to your POST method.
The first thing to use best practice is to use object literals instead of polluting global namespace
`const anyObject = {
// your object
}`
the second thing is to use functional programming techniques
`function( anyObject, argsToDo) {
// do what every you want to anyObject with other functions
// anyObject could contain all the properties you need to write
// () => setWhatever(anyObject)
// or return new anyObjectWithArgsToDo();
}`
you can use the approach to write the fields dynamically for the JSON object that you need to submit example pseudocode:
`function writeFields( anyObject, fieldsObject ) {
let i = 0;
foreach( anyObject as value ) {
value = fieldsObject[I];
i++;
}`
you can combined objects to hold other objects JSON stands for JavaScript Object Notation so you can pass them as an argument to functions, perform operations on them with functions, create new Objects with objects as arguments et. cetera. Here is a link to the Wiki on functional programming https://en.wikipedia.org/wiki/Functional_programming. To use modern programming techniques would be to use a layer of abstraction and encapsulation among other things, it appears as though you are writing to the implementation instead of writing reusable code that performs a task. The best way to write the code is so that it would work with any object, that involves the use of functional programming techniques where the function does not care about the state of it's arguments. Example
`function writeSomeObject ( object, property, value) {
object[${property}] = ${value}; // use back ticks
// that allow dynamic use of arguments
}`
I hope that helps
Related
I am preparing a weather app that gives suggestion list as we enter an input city name.
Json file is in this format:
{"coord":{"lon":-0.13,"lat":51.51},
"weather":[{"id":300,"main":"Drizzle","description":"light intensity drizzle","icon":"09d"}],
"base":"stations",
"main":{"temp":280.32,"pressure":1012,"humidity":81,"temp_min":279.15,"temp_max":281.15},
"visibility":10000,
"wind":{"speed":4.1,"deg":80},
"clouds":{"all":90},"dt":1485789600,
"sys":{"type":1,"id":5091,"message":0.0103,
"country":"GB",
"sunrise":1485762037,
"sunset":1485794875},
"id":2643743,
"name":"London",
"cod":200}
So my code for that functionality is:
inputcity.addEventListener("input",()=>getweather(inputcity.value));
const getweather=async searchtext=>{
const res=await fetch("https://api.openweathermap.org/data/2.5/weather?q="+inputcity.value+"&units=metric&appid=78a661b7c335021c7d85065aea3673be");
const cities= await res.json();
let matches=cities.filter(city=>{
const regex=new RegExp(`^${searchtext}`,'gi');
return city.name.match(regex);
});
console.log(matches);
};
What I want is to show a list of city names starting with same letters.
But it's showing error the following error:
*Uncaught (in promise)TypeError: cities.filter is not a function*
because this api gives data in nested objects not in array of objects.
How can get my code working using filter or any method?
Because i don't have 50 reputation to leave a comment on the comment section.
I will just leave my comments here, so you can got an idea how to do it.
Your code that fetch the API's from https://api.openweathermap.org/data/2.5/weather?q="+inputcity.value+"&units=metric&appid=78a661b7c335021c7d85065aea3673be is only returning data for 1 city.
The returned data is also not a ARRAY, It's a JSON object.
See here for better viewing of your data : https://codebeautify.org/jsonviewer/cb13b4bd
For your idea about suggestion list on city name when users inputted the data, is not on their api's feature.
You should make a array for the city name lookup and shows the suggestion with .startsWith(user.input).
Example super inefficient code to do that :
const userinput = "a";
var city = ["aaa", "abc", "bca", "cba", , "ccc"];
city.forEach(searchsuggestion);
function searchsuggestion(value, index) {
if (value.startsWith(userinput)) {
console.log("Suggestion : " + value);
}
else {
return;
}
}
Your Console output for the above code should be :
Suggestion : aaa
Suggestion : abc
Introduction
I'm learning JavaScript on my own and JSON its something along the path. I'm working on a JavaScript WebScraper and I want, for now, load my results in JSON format.
I know I can use data base, server-client stuff, etc to work with data. But I want to take this approach as learning JSON and how to parse/create/format it's my main goal for today.
Explaining variables
As you may have guessed the data stored in the fore mentioned variables comes from an html file. So an example of the content in:
users[] -> "Egypt"
GDP[] -> "<td> $2,971</td>"
Regions[] -> "<td> Egypt </td>"
Align[] -> "<td> Eastern Bloc </td>"
Code
let countries = [];
for(let i = 0; i < users.length; i++)
{
countries.push( {
'country' : [{
'name' : users[i],
'GDP' : GDP[i],
'Region' : regions[i],
'Align' : align[i]
}]})
};
let obj_data = JSON.stringify(countries, null, 2);
fs.writeFileSync('countryballs.json', obj_data);
Code explanation
I have previously loaded into arrays (users, GDP, regionsm align) those store the data (String format) I had extracted from a website.
My idea was to then "dump" it into an object with which the stringify() function format would format it into JSON.
I have tested it without the loop (static data just for testing) and it works.
Type of error
let obj_data = JSON.stringify(countries, null, 2);
^
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Node'
| property 'children' -> object with constructor 'Array'
| index 0 -> object with constructor 'Node'
--- property 'parent' closes the circle
What I want from this question
I want to know what makes this JSON format "Circular" and how to make this code work for my goals.
Notes
I am working with Node.js and Visual Studio Code
EDIT
This is further explanation for those who were interested and thought it was not a good question.
Test code that works
let countries;
console.log(users.length)
for(let i = 0; i < users.length; i++)
{
countries = {
country : [
{
"name" : 'CountryTest'
}
]
}
};
let obj_data = JSON.stringify(countries, null, 2);
fs.writeFileSync('countryballs.json', obj_data);
});
Notice in comparison to the previous code, right now I am inputing "manually" the name of the country object.
This way absolutely works as you can see below:
Now, if I change 'CountryTest' to into a users[i] where I store country names (Forget about why countries are tagged users, it is out of the scope of this question)
It shows me the previous circular error.
A "Partial Solution" for this was to add +"" which, as I said, partially solved the problem as now there is not "Circular Error"
Example:
for(let i = 0; i < users.length; i++)
{
countries = {
country : [
{
"name" : users[i]+''
}
]
}
};
Resulting in:
Another bug, which I do not know why is that only shows 1 country when there are 32 in the array users[]
This makes me think that the answers provided are not correct so far.
Desired JSON format
{
"countries": {
"country": [
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
},
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
},
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
}
]}
}
Circular structure error occurs when you have a property of the object which is the object itself directly (a -> a) or indirectly (a -> b -> a).
To avoid the error message, tell JSON.stringify what to do when it encounters a circular reference. For example, if you have a person pointing to another person ("parent"), which may (or may not) point to the original person, do the following:
JSON.stringify( that.person, function( key, value) {
if( key == 'parent') { return value.id;}
else {return value;}
})
The second parameter to stringify is a filter function. Here it simply converts the referred object to its ID, but you are free to do whatever you like to break the circular reference.
You can test the above code with the following:
function Person( params) {
this.id = params['id'];
this.name = params['name'];
this.father = null;
this.fingers = [];
// etc.
}
var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him;
JSON.stringify(me); // so far so good
him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
if(key == 'father') {
return value.id;
} else {
return value;
};
})
The answer is from StackOverflow question,
Stringify (convert to JSON) a JavaScript object with circular reference
From your output, it looks as though users is a list of DOM nodes. Rather than referring to these directly (where there are all sort of possible cyclical structures), if you just want their text, instead of using users directly, try something like
country : [
{
"name" : users[i].textContent // maybe also followed by `.trim()
}
]
Or you could do this up front to your whole list:
const usersText = [...users].map(node => node.textContent)
and then use usersText in place of users as you build your object.
If GDP, regions and align are also references to your HTML, then you might have to do the same with them.
EUREKA!
As some of you have mentioned above, let me tell you it is not a problem of circularity, at first..., in the JSON design. It is an error of the data itself.
When I scraped the data it came in html format i.e <td>whatever</td>, I did not care about that as I could simply take it away later. I was way too focused in having the JSON well formatted and learning.
As #VLAZ and #Scott Sauyezt mentioned above, it could be that some of the data, if it is not well formatted into string, it might be referring to itself somehow as so I started to work on that.
Lets have a look at this assumption...
To extract the data I used the cheerio.js which gives you a kind of jquery thing to parse html.
To extract the name of the country I used:
nullTest = ($('table').eq(2).find('tr').eq(i).find('td').find('a').last());
//"Partial solution" for the OutOfIndex nulls
if (nullTest != null)
{
users.push(nullTest);
}
(nullTest helps me avoid nulls, I will implement some RegEx when everything works to polish the code a bit)
This "query" would output me something like:
whatEverIsInHereIfThereIsAny
or else.
to get rid off this html thing just add .html() at the end of the "jquery" such as:
($('table').eq(2).find('tr').eq(i).find('td').find('a').last().html());
That way you are now working with String and avoiding any error and thus solves this question.
So I'm a beginner to js and I have a table of users in a json file and I'm making an account delete feature. I have a find set up to find the user and it works fine but I can't figure out how to make it delete the user from the file, any help would be appreciated!
Json:
{
"users": [
{
"name": "ImBattleDash",
"Id": "780748c5d4504446bbba3114ce48f6e9",
"discordId": "471621420162744342",
"dateAdded": 1548295371
}
]
}
JS:
function findJson() {
fs.readFile('./linkedusers.json', 'utf-8', function (err, data) {
if (err) message.channel.send('Invalid Code.')
var arrayOfObjects = JSON.parse(data)
let findEntry = arrayOfObjects.users.find(entry => entry.discordId == myCode)
let linkEmbed = new Discord.RichEmbed()
.setTitle('Account unlinked!')
.setDescription('Link your account by friending "BattleDash Bot" on Fortnite and then input the code you get messaged by typing "!link <code>"!')
.setColor('#a900ff');
message.channel.send({embed: linkEmbed});
})
}
EDIT: Not sure if it's an array or a table I don't know a lot about json
You need to use:
Array#find to find a given user by some given criteria.
Array#indexOf to get the index of the found user in users
Array#splice to drop one element starting from the index given by Array#indexOf:
const input = {
"users": [
{
"name": "ImBattleDash",
"Id": "780748c5d4504446bbba3114ce48f6e9",
"discordId": "471621420162744342",
"dateAdded": 1548295371
}
]
}
const removeUser = (criteria, users) =>
users.splice (users.indexOf (users.find (criteria)), 1)
removeUser (
({ Id, discordId }) =>
Id == '780748c5d4504446bbba3114ce48f6e9'
&& discordId == '471621420162744342',
input.users
)
// Output: 0 <-- User has been removed!
console.log(input.users.length)
About persisting the change, it's just about calling JSON.stringify (input) and then just write the contents to the desired output file. See this other Q&A: Writing files in Node.js
With great help from Cat and Matias I came up with this code that works!
function findJson() {
fs.readFile('./linkedusers.json', 'utf-8', function (err, data) {
if (err) message.channel.send('Invalid Code.')
var arrayOfObjects = JSON.parse(data)
let findEntry = arrayOfObjects.users.find(entry => entry.discordId == myCode)
const input = arrayOfObjects;
const removeUser = (criteria, users) =>
users.splice (users.indexOf (users.find (criteria)), 1)
removeUser (
({ Id, discordId }) =>
Id == findEntry.Id
&& discordId == findEntry.discordId,
input.users
)
console.log('unlinked')
fs.writeFile('./linkedusers.json', JSON.stringify(arrayOfObjects, null, 4), 'utf-8', function(err) {
if (err) throw err
console.log('Done!')
})
let linkEmbed = new Discord.RichEmbed()
.setTitle('Account unlinked!')
.setDescription('Link your account by friending "BattleDash Bot" on Fortnite and then input the code you get messaged by typing "!link <code>"!')
.setColor('#a900ff');
message.channel.send({embed: linkEmbed});
})
}
Here's a quick tutorial for you:
"Users" would be either an array (using []) or a javascript object (using {}), your choice. There won't be any actual tables unless you use a database instead of a JSON file (although if your JSON expression is as simple as your example, you could almost think of it as a table.) -- And actually, a third option would be to use the javascript Map type, which is like a beefed-up object, but I won't address that here.
While using an array would make it a bit easier to retrieve a list of data for all users (because arrays are simpler to iterate through), using an object would make it considerably easier to retrieve data for a single user (since you can directly specify the user you want by its key instead of needing to loop through the whole array until you find the one you want.) I'll show you an example that uses an object.
The individual user in your sample code is an example of a javascript object. JSON lets you convert an object to a string (for storage, I/O, and human readability) and back to an object (so javascript can understand it). You use the JSON.stringify() and JSON.parse() methods, respectively for these conversions. The string has to be JSON-formatted or this won't work, and your example is almost in JSON format.
To comply with JSON formatting, you could structure a Users object as follows. (Of course we're looking at the stringified version because mere humans can't easily read an "actual" javascript object):
"Users": { // Each individual user is a property of your users object
"780748c5d4504446bbba3114ce48f6e9": // The Id is the key in the "key/value pair"
{ // The individual user object itself is the value in the key/value pair
// Id is duplicated inside user for convenience (not necessarily the best way to do it)
"id": "780748c5d4504446bbba3114ce48f6e9",
"name": "ImBattleDash", // Each property of the user is also a key/value pair
"discordId": "471621420162744342", //Commas separate the properties of an object
"dateAdded": "1548295371" // All property values need double quotes for JSON compatibility
}, // Commas separate the properties (ie the individual users) of the users object
"446bbba3114ce48f6e9780748c5d4504": // This string is the second user's key
{ // This object is the second user's value
"id": "446bbba3114ce48f6e9780748c5d4504",
"name": "Wigwam",
"discordId": "162744342471621420",
"dateAdded": "1548295999"
}
}
Once you retrieve the string from storage, you convert it to an object and delete a user as follows. (This is broken down into more steps than necessary for clarity.):
let usersObject = JSON.parse(stringRetrievedFromFile);
let userId = "780748c5d4504446bbba3114ce48f6e9";
let userToModifyOrDelete = usersObject[userId];
delete userToModifyOrDelete;
To change the user's discordId instead, you would do:
let discordId = userToModifyOrDelete.discordId; // Not necessary, just shows how to retrieve value
let newDiscordId = "whateverId";
userToModifyOrDelete.discordId = newDiscordId;
And you'd convert the object back into a string to store in your file with:
JSON.stringify(usersObject);
Hopefully that's almost all you need to know about JSON!
I have an object coming to my VueJS front-end and I need to assign it to a large form with many v-models, which will then submit a new object to the backend. The NEW PARCEL in this photo represents the v-models and the OLD PARCEL is how it is coming from the backend:
My problem is I can't find a way to assign the properties to the NEW PARCEL accurately without doing it line by line, as the properties are nested differently based on address and keyedData:
this.newParcel.state = parcel.address.parsed_state
this.newParcel.zip = parcel.address.parsed_postal
this.newParcel.onSiteContactName = parcel.keyedData.onSiteContactName
^ Only 4/52! If anyone notices a simpler way to do this, my code would love it! It's quite gnarly to have so many lines of code to achieve the desired effect.
this.newParcel.onSiteContactEmail = parcel.keyedData.onSiteContactEmail
To make assigning a new structure less verbose, you could create some reference fields, and assign a new object using those.
eg..
const parcel = {
address: {
parsed_state: "State",
parsed_postal: "Postal"
},
keyedData: {
onSiteContactName: "On site contact name"
}
};
const addr = parcel.address; //ref var for address
const keyd = parcel.keyedData; //ref var for keyedData
const newParcel = {
state: addr.parsed_state,
zip: addr.parsed_postal,
onSiteContactName: keyd.onSiteContactName
};
console.log(newParcel);
Given the following json result, how can I convert the validationErrors key/value property into a nice array object in javascript so i can then do things like
errors[0].Key
or
errors[0].Value, etc...
NOTE: If it's easier to do the conversion with jQuery, then I'm happy to use that. Also, I'm getting the data via jQuery -> $.post...)
update:
Here's the actual json data so someone can answer this with JSFiddle please.
{
"aaaa": 0,
"bbbb": 0,
"cccc": null,
"validationErrors": {
"a1_7127763-1c7ac823-61d5-483f-a9ca-4947e9eb8145": "Invalid PropertyType. Please choose any property except Unknown.",
"a2_7127763-1c7ac823-61d5-483f-a9ca-4947e9eb8145": "A State is required. Eg. Victoria or New South Wales.",
"b1_5433417-18b5568a-d18e-45e2-9c63-30796995e2d3": "Invalid PropertyType. Please choose any property except Unknown.",
"b2_5433417-18b5568a-d18e-45e2-9c63-30796995e2d3": "A State is required. Eg. Victoria or New South Wales.",
"c1_6655305-297c57f9-a460-4101-be7d-70c6b9a565d5": "Invalid PropertyType. Please choose any property except Unknown.",
"c2_6655305-297c57f9-a460-4101-be7d-70c6b9a565d5": "A State is required. Eg. Victoria or New South Wales."
}
}
I would take all the keys of the object and then map them to an array.
var arrayOfErrors = Object.keys(objectOfErrors).map(function(errorKey) {
return objectOfErrors[errorKey];
});
You can use map to convert the object to an array:
var errors = jQuery.map(data.validationErrors, function (value, key) {
return {
"Key": key,
"Value": value
};
});
JSFiddle showing this approach: http://jsfiddle.net/WgaFb/1/
If you do not wish to use jQuery, here is a pure JavaScript method:
var errors = [];
for(var key in data.validationErrors) {
errors.push({
"Key": key,
"Value": data.validationErrors[key]
});
}
JSFiddle for this second approach: http://jsfiddle.net/4WXEF/1/