I am trying to strip out the display name from an email, e.g.
Steve<steve#steve.com> to steve#steve.com
function test1() {
var testemail = ["Steve<steve#steve.com>","displayname<display#steve.com>"];
var debug = stripEmail(testemail);
var debug9 = "";
}
function stripEmail(email) {
//Give me an email with a display name and I will strip out the display name
//"<Steve Gon> stevegon#google.com"
if (typeof email === 'string') {
var arr = [email];
} else {
var arr = email;
}
for (i=0; i<arr.length; i++) {
if (arr[i].search("<")>-1) {//If there is no less than, then it doesn't have a display name
var part1 = arr[i].split("<");
if (part1.length == 2) {
arr[i] = part1[1].replace(">","");
arr[i] = arr[i].replace("<","");
arr[i] = arr[i].replace(" ","");
}
}
}
return arr;
}
Once the code steps out of stripEmail, the argument email is changed to the result. I've notice this happening in some other functions as well. This is resulting in strange problems when I try to use the variables.
Variable testemail set:
Once I step over the function, testemail is changed.
Avoid setting one array equal to another array if you want to avoid changing the original array. Instead of reusing the email variable, create a new output variable. In the code below, the emails are put into a new array named result.
The stripEmail function handles both a string and an array, and always returns an array that is different than the source array, leaving the original array unchanged.
The code can be changed to:
function test1() {
var testemail = ["Steve<steve#steve.com>","displayname<display#steve.com>"];
Logger.log('testemail: ' + testemail)
var debug = stripEmail(testemail);
Logger.log('debug: ' + debug)
Logger.log('testemail: ' + testemail)
}
function stripEmail(email) {
var arr,i,part1,result;
result = [];
//Give me an email with a display name and I will strip out the display name
//"<Steve Gon> stevegon#google.com"
if (typeof email === 'string') {
email = email.split(",");
}
for (i=0; i<email.length; i++) {
if (email[i].search("<")>-1) {//If there is no less than, then it doesn't have a display name
part1 = email[i].split("<");
if (part1.length == 2) {
result[i] = part1[1].replace(">","");
result[i] = result[i].replace("<","");
result[i] = result[i].replace(" ","");
}
}
}
return result;
}
Related
The goal of this "counter" is to see how many different words are inside the "ToCount"string. To do that it makes ToCount an array and then iterates over the elements, checking if they already are there and if not, adding them there.
The ToCountArr2 remains empty after the loop and a length of 0 is being displayed. Why does that happen and what can I do about it?
I ran a debugger and saw that no elements are added to the second list, as if nothing appenned inside the "if" control if the i-th element of the first array already is inside the second array.
function counter(){
var ToCount = document.getElementById("demo").value; //the contents of a textbox
var ToCountArr1 = ToCount.split(" ");
var ToCountArr2 = new Array;
var i = 0;
var lengthToCountArr1 = ToCountArr1.length;
var wordToPush;
while (i < lengthToCountArr1){
if(ToCountArr2.includes(ToCountArr1[i] === false)) {
wordToPush = ToCountArr1[i];
ToCountArr2.push(wordToPush);
}
i = i + 1;
}
alert(ToCountArr2.length);
}
The issue is with this line if(ToCountArr2.includes(ToCountArr1[i] === false)). Here the braces need to be after ToCountArr1[i], where as this line ToCountArr1[i] === false) is checking whether that value in ToCountArr1 is true or false.
This line
if(ToCountArr2.includes(ToCountArr1[i] === false)) will be evaluated as
if(ToCountArr2.includes(true/false))
depending on result of ToCountArr1[i] === false)
function counter() {
var ToCount = document.getElementById("demo").value; //the contents of a textbox
var ToCountArr1 = ToCount.split(" ");
var ToCountArr2 = new Array;
var i = 0;
var lengthToCountArr1 = ToCountArr1.length;
var wordToPush;
while (i < lengthToCountArr1) {
if (ToCountArr2.includes(ToCountArr1[i]) === false) {
wordToPush = ToCountArr1[i];
ToCountArr2.push(wordToPush);
}
i = i + 1;
}
console.log(ToCountArr2.length);
}
counter()
<input type='text' id='demo' value='Test Values'>
You can minimize if (ToCountArr2.includes(ToCountArr1[i]) === false) { by replacing it with
if (!ToCountArr2.includes(ToCountArr1[i])) {
Your wordcount function should use a parameter so you can pass a string in. This means you can use the wordcount function on an any string, not just the "demo" element. Also, this is a good time to learn about Map -
const wordcount = (str = "") =>
{ const result =
new Map
for (const s of str.split(/ /))
if (s === "")
continue
else if (result.has(s))
result.set(s, result.get(s) + 1)
else
result.set(s, 1)
return Array.from(result.entries())
}
const prettyPrint = (value) =>
console.log(JSON.stringify(value))
<!-- pass this.value as the string for wordcount
-- wordcount returns a value that we could use elsewhere
-- prettyPrint displays the value to the console
-->
<input onkeyup="prettyPrint(wordcount(this.value))">
Run the code snippet and copy/paste the following line into the field -
this is the captain speaking. is this the commander?
You will see this output -
[["this",2],["is",2],["the",2],["captain",1],["speaking.",1],["commander?",1]]
Here is an working example. I think it will help you right way. Here I use indexOf to check the value exist on a array.
function counter(){
var ToCount = "I am string just for text.";//document.getElementById("demo").value; //the contents of a textbox
var ToCountArr1 = ToCount.split(" ");
var ToCountArr2 = new Array;
var i = 0;
var lengthToCountArr1 = ToCountArr1.length;
var wordToPush;
while (i < lengthToCountArr1){
if( ToCountArr2.indexOf(ToCountArr1[i]) == -1 ) {
wordToPush = ToCountArr1[i];
ToCountArr2.push(wordToPush);
}
i = i + 1;
}
alert(ToCountArr2.length);
}
counter();
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.
This is problem-solving q; nothing's gone wrong, I'm just stumped about how to advance. Basically I want my users to be able to
Point to an arbitrary key of an object with arbitrary depth via a string representation of the "path";
Confirm that each step of the "path" exists; and
Implement CRUD-like functionality
I can verify that each key is valid, but I'm just stumped on how to then utilize said path without ultimately using an eval() statement, but of course I needn't explain why I'm not going to let a call to eval() get anywhere near arbitrary user input. Here's as far as I can get:
const SEP = "/" //in reality this is set by the server,
MyObjInterface = function() {
this.params = {};
this.response = {};
// suppose some initialization business, then on to the question... ( >.>)
this.updateOb= function(path, value ) {
path = path.replace('\\' + DS,"$DIRECTORY_SEPARATOR").split(DS);
for (var i = 0; i < path.length; i++) {
path[i].replace("$DIRECTORY_SEPARATOR",DS);
}
if (typeof(path) != "object" || path.length < 3) { // 3 = minimum depth to reach a valid field in any rset scheme
throw "Could not create rset representation: path either incorrectly formatted or incomplete."
}
var invalidPath = false;
var searchContext = path[0] === "params" ? this.params : this.response;
for (var i = 1; i < path.length - 1; i++) {
if (invalidPath) { throw "No key found in rset at provided path: [" + path[i] + "]."; }
if (i === path.length - 1) {
searchContext[path[1]] = value;
return true;
}
if (path[i] in searchContext) {
searchContext = searchContext[path[i]];
} else {
invalidPath = true;
}
}
}
};
You break the path up in to components, then you recursively walk the tree.
My JS is weak, so I'll pseudo code.
path = "go.down.three";
listOfElements = breakUpPath(path); // Now listOfElement = ["go", "down", "three"];
myObj = setObjectValue(myObj, listOfElement, "I'm a value");
function setObjectValue(obj, listOfElements, value) {
var firstPart = pop(listOfElements); // get and remove top of listOfElements stack
if (length(listOfElements) == 0) { // last element of the path, set the value
obj[firstPart] = value;
return obj;
} else {
// check if a property exists at all
var firstValue = obj[firstPart];
if (firstValue == null) {
firstValue = new Object();
obj[firstPart] = firstValue;
}
obj[firstPart] = setObjectValue(firstValue, listOfElement, value);
}
}
So, my as I said, my JS is weak (really weak, I can't really even spell JavaScript). Needless to say, this is untested.
But something like that is what you're looking for. Just work your way down the elements of the path.
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;
}
I need to sort through a data set which as you can see I've assigned to the records variable. From that data I need to see if the zip code exists. If the zip code does not exist then I need to move it into the array (There of course will be duplicates) and continue checking the rest of the records, if it does exist I need to do nothing.
// Declare Array
var numbersArray = [];
// Variables
var records;
var zipCode;
var numbers;
var index;
var output;
var outputMessageOne;
var outputMessageTwo;
var count = 0;
output = document.getElementById('outputDiv');
records = openZipCodeStudyRecordSet();
output.innerHTML = "The unique zip codes are: ";
while (records.readNextRecord()) {
zipCode = records.getSampleZipCode();
for (index = 0; index < numbersArray.length; index++) {
if (zipCode === numbersArray[index]) {
var uniqueZip = false;
break;
records++;
}
if (zipCode !== numbersArray[index]) {
numbersArray.push(zipCode);
}
}
output.innerHTML += numbersArray;
}
}
You can simplify your for loop like so:
matchedZip = false;
for(i in numbersArray) {
if (numbersArray[i] === zipCode) {
matchedZip = true;
}
}
if ( ! matchedZip) {
numbersArray.push(zipCode);
}
Try plugging that into your while loop. If you have the array push inside of the for loop you're going to end up pushing each zip code in every time there is not a match.
Well, you didn't exactly ask a question, but I'll answer anyway :) The answer is that you should not use a normal array for this, but rather a map or associative array. Fortunately a plain Javascript object can be used for this:
var numbers = {};
// Variables
var records;
var numbers;
var index;
var output;
var outputMessageOne;
var outputMessageTwo;
var count = 0;
output = document.getElementById('outputDiv');
records = openZipCodeStudyRecordSet();
output.innerHTML = "The unique zip codes are: ";
while (records.readNextRecord()) {
var zipCode = records.getSampleZipCode();
numbers[zipCode] = 1; // just picking an arbitrary value
}
for (var zipCode: numbers) {
output.innerHTML += zip + " ";
}
The reason is that this way you don't need to loop through the existing data for each new input.