Why can't I ready this Object properties? - javascript

I have been trying to figure this out for a couple hours now.
I am receiving a JSON post (Shopify), I'm working in app script via doPost(e) web app, but maybe that's irrelevant.
Here's how it looks when received
notice the weird backslashes in postData.contents (it was stringified to show in my console)...
{"contentLength":6450,"queryString":"","postData":{"contents":"{\"id\":820982911946154508,\"
// clipped because stackoverflow doesn't like it
Here are a bunch of things I've tried
e.postData.contents;
// => {"id":820982911946154508,"email":"jon#doe.ca",...} (Clipped here too)
//Great, looks like a proper object...BUT...
var order = e.postData.contents;
//order.id => undefined
var order = JSON.parse(e.postData.contents);
//order.id => undefined
var order = JSON.parse(JSON.stringify(e.postData.contents));
//order.id => undefined

This is how I convert all numbers over 9 digits to strings
Not the best, maybe if a substring contains commas or colons that surround a number, it will break it.
function testStringNum () {
var test = {"id":820982911946154500,"email":"jon#doe.ca","closed_at":null,"created_at":"2020-07-01T10:01:14-07:00","updated_at":"2020-07-01T10:01:14-07:00","number":234 };
var result = jsonStringNums(test);
}
function jsonStringNums(obj) {
var objString = JSON.stringify(obj);
var objArray = objString.split(/[:,]+/);
for (let index in objArray) {
var regExp = new RegExp(/^(?=.*\d)[\d ]+$/);
var isNum = regExp.test(objArray[index]);
if (isNum && objArray[index].length >= 9) {
objString = objString.replace(objArray[index], '"'+objArray[index]+'"');
}
}
return JSON.parse(objString);
}
Had to handle duplicate data in the array, so this is better
function jsonStringNums(obj) {
var objString = JSON.stringify(obj);
var objArrayAll = objString.split(/[:,]+/);
var objArray = objArrayAll.reduce((unique,item) => {
return unique.includes(item) ? unique : [...unique,item] }, []);
var rxAllNumAndSpaces = new RegExp(/^(?=.*\d)[\d ]+$/);
for (let index in objArray) {
var isNum = rxAllNumAndSpaces.test(objArray[index]);
if (isNum && objArray[index].length >= 8) {
var replacement = '\"'+objArray[index]+'\"';
var rxAllVarInsts = new RegExp(objArray[index], 'g');
objString = objString.replace(rxAllVarInsts, replacement);
}
}
try {
return JSON.parse(objString);
} catch (err) { Logger.log('you done messed up your object '+err);return obj; }
}

Related

Getting Incorrect range height for seemingly no reason?

I am writing a script to copy and paste a range from one sheet to another. The pasted range size should be reduced by using two functions : one to delete rows with specific values and the other is an aggregate function.
I started getting this error after I introduced the aggregate function The function is basically reducing the array size using the reduce JS function.
I have replicated my problem here and the code is accessible in the script editor.
When I run the script I am getting the following error :
Incorrect range height was 28 but should be 7 (line 36, file "test")
I have no idea why am I getting this error. My aggregate function returns a properly formatted array with the right length.
function append_range(){
var origin_sheet = SpreadsheetApp.openById('1-2ZheMz1p01qwtwY3ghbNjJedYfGXeylnLEjDMCLpMw');//open the file
origin_sheet = origin_sheet.getSheetByName('test');
var rangeStart = 2;
var range = origin_sheet.getRange('A'+ (rangeStart.toString())+':T'+ (origin_sheet.getLastRow()).toString());
var dataFromRange = range.getValues();
var dataFromRangeLength = dataFromRange.length;
var destination_sheet = SpreadsheetApp.openById('1-2ZheMz1p01qwtwY3ghbNjJedYfGXeylnLEjDMCLpMw');
destination_sheet = destination_sheet.getSheetByName('append');
var rowLast = destination_sheet.getLastRow()+1;
Logger.log("row last" + rowLast);
var formattedRange = deleteRows(dataFromRange);
var groups = aggregate(formattedRange);
var aggregates = [];
for(var group in groups)
{
aggregates.push(groups[group]);
}
Logger.log(aggregates);
var formattedRangeLength = aggregates.length;
Logger.log("formattedRangeLength" + formattedRangeLength);
destination_sheet.getRange(rowLast,1,formattedRangeLength, 20).setValues(deleteRows(dataFromRange));
function isDate(sDate) {
if (isValidDate(sDate)) {
sDate = Utilities.formatDate(new Date(sDate), "PST", "yyyy-MM-dd");
}
return sDate;
}
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" )
return false;
return !isNaN(d.getTime());
}
//
function deleteRows(dataRange){//just pass the range in an array and this method will return another array with filtered range
var formatted = dataRange.filter(function(e) {
return e[8]||e[9]||e[10]||e[11]||e[12]||e[13]||e[14]||e[15]||e[16]||e[17]||e[18]||e[19];
});
return formatted;
}
function aggregate(data)
{
var groups = data.reduce(
function(accumulator, previous){
{
var key = previous[1] + previous[3] + previous[5] + previous[6];
var group = accumulator[key];
if(group == null || typeof group == 'undefined')
{
accumulator[key] = previous;
}
else {
var startIndex = 8;
for(var i = startIndex; i < previous.length;i++)
{
group[i] += previous[i];
}
}
return accumulator;
}},
{});
return groups;
}
}
The .setValues() is not setting your aggregates array it is trying to set deleteRows(dataFromRange)
// Change the setValues() to your reduced array
destination_sheet.getRange(rowLast,1,formattedRangeLength, 20).setValues(aggregates);
I think this might work:
var output=deleteRows(dataFromRange));
destination_sheet.getRange(rowLast,1,output.length, output[0].length).setValues(deleteRows(output));
This assumes a non jagged array.

Javascript Function to split and return a value from a string

I am trying to grab a certain value. I am new to javascript and I can't figure out why this is not working.
If I parse "kid_2" I should get "kostas". Instead of "Kostas" I always get "02-23-2000". So I must have a logic problem in the loop but I am really stuck.
function getold_val(fieldname,str){
var chunks=str.split("||");
var allchunks = chunks.length-1;
for(k=0;k<allchunks;k++){
var n=str.indexOf(fieldname);
alert(chunks[k]);
if(n>0){
var chunkd=chunks[k].split("::");
alert(chunkd);
return chunkd[1];
}
}
}
var test = getold_val('kid_2','date_1::02-23-2000||date_2::06-06-1990||kid_1::George||kid_2::Kostas||');
alert(test);
A regex may be a little more appealing. Here's a fiddle:
function getValue(source, key){
return (new RegExp("(^|\\|)" + key + "::([^$\\|]+)", "i").exec(source) || {})[2];
}
getValue("date_1::02-23-2000||date_2::06-06-1990||kid_1::George||kid_2::Kostas||","kid_2");
But if you want something a little more involved, you can parse that string into a dictionary like so (fiddle):
function splitToDictionary(val, fieldDelimiter, valueDelimiter){
var dict = {},
fields = val.split(fieldDelimiter),
kvp;
for (var i = 0; i < fields.length; i++) {
if (fields[i] !== "") {
kvp = fields[i].split(valueDelimiter);
dict[kvp[0]] = kvp[1];
}
}
return dict;
}
var dict = splitToDictionary("date_1::02-23-2000||date_2::06-06-1990||kid_1::George||kid_2::Kostas||","||","::");
console.log(dict["date_1"]);
console.log(dict["date_2"]);
console.log(dict["kid_1"]);
console.log(dict["kid_2"]);​
This works, here's my fiddle.
function getold_val(fieldname,str) {
var chunks = str.split('||');
for(var i = 0; i < chunks.length-1; i++) {
if(chunks[i].indexOf(fieldname) >= 0) {
return(chunks[i].substring(fieldname.length+2));
}
}
}
alert(getold_val('kid_2', 'date_1::02-23-2000||date_2::06-06-1990||kid_1::George||kid_2::Kostas||'));
The issue with your code was (as #slebetman noticed as well) the fact that a string index can be 0 because it starts exactly in the first letter.
The code is almost the same as yours, I just didn't use the second .split('::') because I felt a .substring(...) would be easier.
There are two bugs. The first error is in the indexOf call:
var n = str.indexOf(fieldname);
This will always return a value greater than or equal to 0 since the field exists in the string. What you should be doing is:
var n = chunks[k].indexOf(fieldname);
The second error is in your if statement. It should be:
if(n >= 0) {
...
}
or
if(n > -1) {
...
}
The substring you are looking for could very well be the at the beginning of the string, in which case its index is 0. indexOf returns -1 if it cannot find what you're looking for.
That being said, here's a better way to do what you're trying to do:
function getold_val(fieldName, str) {
var keyValuePairs = str.split("||");
var returnValue = null;
if(/||$/.match(str)) {
keyValuePairs = keyValuePairs.slice(0, keyValuePairs.length - 1);
}
var found = false;
var i = 0;
while(i < keyValuePairs.length && !found) {
var keyValuePair = keyValuePairs[i].split("::");
var key = keyValuePair[0];
var value = keyValuePair[1];
if(fieldName === key) {
returnValue = value;
found = true;
}
i++;
}
return returnValue;
}

How can this Javascript be expressed more succinctly?

I have some Python code that I'm porting to Javascript:
word_groups = defaultdict(set)
for sentence in sentences:
sentence.tokens = stemmed_words(sentence.str_)
for token in sentence.tokens:
word_groups[sentence.actual_val].add(token)
I don't know a lot about Javascript, so this was the best I could do:
var word_groups = {}
for(var isent = 0; isent < sentences.length; isent++) {
var sentence = sentences[isent]
sentence.tokens = stemmed_words(sentence.str_)
for(var itoken = 0; itoken < sentence.tokens.length; itoken++) {
var token = sentence.tokens[itoken]
if(!(sentence.actual_val in word_groups))
word_groups[sentence.actual_val] = []
var group = word_groups[sentence.actual_val]
if(!(token in group))
group.push(token)
}
}
Can anyone suggest ways to make the javascript code look more like the python?
I'm going to assume that if you're using an environment where forEach is available, reduce and Object.keys are available as well. (e.g. ECMAScript >= 1.8.5):
var word_groups = sentences.reduce(function (groups, sentence) {
var val = sentence.actual_val
var group = groups[val] = groups[val] || []
stemmed_words(sentence.str_).forEach(function (t) {
if (!(t in group)) group.push(t)
})
return groups
}, {})
It's quite possible that I've misinterpreted what your Python code does, but assuming you're after word counts, I'd write it as follows:
var word_groups = {}
sentences.forEach(function (sentence) {
sentence.tokens = stemmed_words(sentence.str_)
sentence.tokens.forEach(function (token) {
var val = sentence.actual_val
word_groups[val] = (word_groups[val] || 0) + 1
})
})
The above will fail should the word "constructor" appear in the input. It's possible to work around this JavaScript quirk:
var word_groups = {}
sentences.forEach(function (sentence) {
sentence.tokens = stemmed_words(sentence.str_)
sentence.tokens.forEach(function (token) {
var val = sentence.actual_val
if (!word_groups.hasOwnProperty(val)) word_groups[val] = 0
word_groups[val] += 1
})
})
If you're not definitely in Javascript 1.6 or higher (notable IE 8 has Javascript 1.5) you may want jQuery as a compatibility layer. For example $.each(a, f) is compatible with a.forEach(f).

Append number to a comma separated list

the list looks like:
3434,346,1,6,46
How can I append a number to it with javascript, but only if it doesn't already exist in it?
Assuming your initial value is a string (you didn't say).
var listOfNumbers = '3434,346,1,6,46', add = 34332;
var numbers = listOfNumbers.split(',');
if(numbers.indexOf(add)!=-1) {
numbers.push(add);
}
listOfNumbers = numbers.join(',');
Basically i convert the string into an array, check the existence of the value using indexOf(), adding only if it doesn't exist.
I then convert the value back to a string using join.
If that is a string, you can use the .split() and .join() functions, as well as .push():
var data = '3434,346,1,6,46';
var arr = data.split(',');
var add = newInt;
arr.push(newInt);
data = arr.join(',');
If that is already an array, you can just use .push():
var data = [3434,346,1,6,46];
var add = newInt;
data.push(add);
UPDATE: Didn't read the last line to check for duplicates, the best approach I can think of is a loop:
var data = [3434,346,1,6,46];
var add = newInt;
var exists = false;
for (var i = 0; i < input.length; i++) {
if (data[i] == add) {
exists = true;
break;
}
}
if (!exists) {
data.push(add);
// then you would join if you wanted a string
}
You can also use a regular expression:
function appendConditional(s, n) {
var re = new RegExp('(^|\\b)' + n + '(\\b|$)');
if (!re.test(s)) {
return s + (s.length? ',' : '') + n;
}
return s;
}
var nums = '3434,346,1,6,46'
alert( appendConditional(nums, '12') ); // '3434,346,1,6,46,12'
alert( appendConditional(nums, '6') ); // '3434,346,1,6,46'
Oh, since some really like ternary operators and obfustically short code:
function appendConditional(s, n) {
var re = new RegExp('(^|\\b)' + n + '(\\b|$)');
return s + (re.test(s)? '' : (''+s? ',':'') + n );
}
No jQuery, "shims" or cross-browser issues. :-)

extract GET parameters from a user inputed url with javascript

I am looking to use javascript to extract the GET parameters from a user inputed url.
For example is a user enters a url say:
http://www.youtube.com/watch?v=ee925OTFBCA
I could get the v parameter
'ee925OTFBCA' as a variable
Thanks in Advance.
This should do the trick
// include this somewhere available
var Query = (function(){
var query = {}, pair, search = location.search.substring(1).split("&"), i = search.length;
while (i--) {
pair = search[i].split("=");
query[pair[0]] = decodeURIComponent(pair[1]);
}
return query;
})();
var v= Query["v"]
This only runs its computation once and creates an object with name/value pairs corresponding to those supplied as parameters
From here:
function getURLParam(strParamName){
var strReturn = "";
var strHref = window.location.href;
if ( strHref.indexOf("?") > -1 ){
var strQueryString = strHref.substr(strHref.indexOf("?")).toLowerCase();
var aQueryString = strQueryString.split("&");
for ( var iParam = 0; iParam < aQueryString.length; iParam++ ){
if (
aQueryString[iParam].indexOf(strParamName.toLowerCase() + "=") > -1 ){
var aParam = aQueryString[iParam].split("=");
strReturn = aParam[1];
break;
}
}
}
return unescape(strReturn);
}
To use it:
var v = getURLParam('v')
You can use a function like this:
function querystring(key) {
var re=new RegExp('(?:\\?|&)'+key+'=(.*?)(?=&|$)','gi');
var r=[], m;
while ((m=re.exec(document.location.search)) != null) r.push(m[1]);
return r;
}
Example:
var v = querystring('v')[0];
The function returns an array with all the values found in the query string. If you have a query string like ?x=0&v=1&v=2&v=3 the call querystring('v') returns an array with three items.
This is my simple snippet:
function extractParamValue(url, name) {
var pos = url.indexOf(name+'=')+name.length+1;
var value = url.substring(pos, url.indexOf('&', pos));
return value;
}

Categories

Resources