Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Saturday, July 20, 2019

[JavaScript] Testing (and Timing) String Reversal Functions

So Sarah Chima wrote an article about reversing a string, done four different ways. A few folk wrote in with other solutions. I wrote in with some too.

Then it was suggested that we try to work out which really is the fastest. What follows is me trying.

So I need first of all to mention my working environment. It's call Lychen and it's a wrapping of the V8 JavaScript engine in a C# command line application with access to some C# objects, methods and properties. Lychen is «not supposed to be cutting-edge. Rather, it's on the spine of the blade, about as far from the cutting-edge as one can get without leaving the blade altogether.» (see the Wiki).

You might say to me, "Hey, what about node?" My response is usually along the lines of "I just can't get my head around the promises and the asynchrony. Maybe one day."

So here's the code.
 
const Console = CS.System.Console;

CS. exposes a large number of C# core and third party objects into the Lychen (V8) environment. Rather than keep typing CS.System.Console we create an abbreviation.
 
if (CSSettings.ContainsKey("/D")) {
  debugger;
}

On launch, CSSettings (a Dictionary) receives all the command line parameters. In this case, if it's /D we debug.

const times = CSSettings.ContainsKey("/TIMES") ? parseInt(CSSettings("/TIMES"), 10) : 1000;

Similarly, here we check for the presence of /TIMES and if it's, say, /TIMES:123 then times is set to 123. Otherwise times defaults to 1000. /TIMES exists because we want to be able to run each test many times.

The first test of any routine usually takes a bit longer than subsequent runs due to operating system caching. We'll take many measurements and then average them in the hope of getting a better idea of how long things really take.
 
var original;
if (CSSettings.ContainsKey("/RANDOM")) {
  original = Array(12500)
    .fill(0)
    .map(function () {
      return String.fromCharCode(Math.floor(Math.random() * 256));
    }).join("");
} else {
  original = Array(500).join("lewd did i live - evil i did dwel").substr(0, 12500);
}

If the command-line contains /RANDOM we generate a test string of 12500 random ASCII characters. Otherwise we fill an array with some text and then truncate it to 12500 characters. 12500 was chosen because larger numbers caused the recursive functions to fail impolitely.
 
var reversed = Sarah_ForOf(original);

We use one of the following reversal functions to reverse the original test string so that we can double check that the reversal actually WAA (Works As Advertised).
 
function TimeTest(name, fn, original) {
  var Stopwatch = new CS.System.Diagnostics.Stopwatch();
  Stopwatch.Start();
  var answer = fn(original);
  Stopwatch.Stop();
  var ts = Stopwatch.Elapsed;
  return {
    name: name,
    ticks: ts.Ticks,
    reversed: answer
  };
}

We use C#'s System.Diagnostics.Stopwatch to track the run time of the function being tested. The parameters are: the name of the function, the function's reference, and the string to be tested. The Ticks of the Elapsed result of the run are returned along with the name and the results of the reversal. More about Ticks at the end.
 
function EmptyFunction(string) {
  return string;
}

We want to account for the cost of actually making the call, so we will time how long it takes just to load run an empty function that returns a string.

Next come the contributed routines.
 
const Sarah_SplitReverseJoin = (string) => string.split("").reverse().join('');

const Nathanael_SplitReverseJoin = (string) => [...string].reverse().join('');

function Sarah_ForOf(string) {
  let reverseString = "";
  for (let character of string) {
    reverseString = character + reverseString;
  }
  return reverseString;
}

const Sarah_Reduce = (string) => string.split('').reduce((rev, char) => char + rev, '')

function Sarah_Recursive(string) {
  return string ? Sarah_Recursive(string.substring(1)) + string[0] : string;
}

function Theophanis_SplitFor(string) {
  let result = string.split('');
  for (let i = 0, j = string.length - 1; i < j; i++, j--) {
    result[i] = string[j];
    result[j] = string[i];
  }
  return result.join('');
}

function Theophanis_SplitFor_Bruced(string) {
  let result = string.split('');
  for (let i = 0, j = string.length - 1; i < j; i++, j--) {
    const string_i = string[i];
    const string_j = string[j];
    if (result[i] !== string_j) {
      result[i] = string_j;
    }
    if (result[j] !== string_i) {
      result[j] = string_i;
    }
  }

  return result.join('');
}

I thought that checking for the need to swap before actually swapping would be a good optimisation. I was wrong, especially with respect to random data
 
function Bruce_ArrayApplyMap(string) {
  return Array.apply(null, new Array(string.length).fill(0).map(function (_, i) {
      return string.charAt(string.length - 1 - i);
    })).join("");
}

function Bruce_MapSortMap(string) {
  return Array(string.length)
  .fill({})
  .map(function (item, index) {
    return {
      index: index,
      character: string.charAt(index)
    };
  }).sort(function (a, b) {
    return a.index > b.index ? -1 : (a.index === b.index ? 0 : 1);
  }).map(function (item) {
    return item.character;
  }).join("");
}

function Bruce_Recursive1(string) {
  return (string.length === 1)
   ? string
   : Bruce_Recursive1(string.substr(1)) + string.substr(0, 1);
}

function Bruce_Recursive2(string) {
  if (1 >= string.length)
    return string;
  return (
    string.substr(-1) +
    Bruce_Recursive2(string.substr(1, string.length - 2)) +
    string.substr(0, 1));
}

function Bruce_CharAt(string) {
  const result = Array(string.length);
  for (let i = string.length - 1, j = 0; i >= 0; i--, j++) {
    result[j] = string.charAt(i);
  }
  return result.join("");
}

function Bruce_CharAt2(string) {
    const result = Array(string.length).fill(1);
    result.map(function (item,index) {
        let rhs = string.length - 1 - index;
        result[index] = string.charAt(index);
    });
    return result.join("");
}

That's all the contributed functions.

const namesAndCodes = [{
    name: "Sarah_SplitReverseJoin",
    code: Sarah_SplitReverseJoin
  }, {
    name: "Sarah_ForOf",
    code: Sarah_ForOf
  }, {
    name: "Sarah_Reduce",
    code: Sarah_Reduce
  }, {
    name: "Sarah_Recursive",
    code: Sarah_Recursive
  }, {
    name: "Theophanis_SplitFor",
    code: Theophanis_SplitFor
  }, {
    name: "Theophanis_SplitFor_Bruced",
    code: Theophanis_SplitFor_Bruced
  }, {
    name: "Nathanael_SplitReverseJoin",
    code: Nathanael_SplitReverseJoin
  }, {
    name: "Bruce_ArrayApplyMap",
    code: Bruce_ArrayApplyMap
  }, {
    name: "Bruce_MapSortMap",
    code: Bruce_MapSortMap
  }, {
    name: "Bruce_Recursive1",
    code: Bruce_Recursive1
  }, {
    name: "Bruce_Recursive2",
    code: Bruce_Recursive2
  }, {
    name: "Bruce_CharAt",
    code: Bruce_CharAt
  }, {
    name: "Bruce_CharAt2",
    code: Bruce_CharAt2
  }
];

The names and functions to be tested.
 
var gathering = {};

for (let i = 0; i < times; i++) {
  namesAndCodes.forEach(function (item) {
    const eps = TimeTest("EmptyFunction", EmptyFunction, original).ticks;
    const result = TimeTest(item.name, item.code, original);
    if (!gathering[result.name]) {
      gathering[result.name] = [];
    }
    gathering[result.name].push(result.ticks - eps);
  });
}

Here we do the testing, looping from zero to whatever value times holds. We forEach through the namesAndCodes structure. We calculate the time it takes to run an empty function and then we subtract that from the ticks of the result of the test. gathering holds the result of each test in an array keyed to the name of the function.
 
const average = arr => arr.reduce((p, c) => p + c, 0) / arr.length;

Object.keys(gathering).map(function (item) {
  return [item, average(gathering[item])];
}).sort(function (a, b) {
  return a[1] > b[1] ? 1 : a[1] === b[1] ? 0 : -1;
}).forEach(function (item) {
  Console.WriteLine("{0,-28}{1} ticks", item[0], item[1]);
});
""

Report on the results: Convert the gathering object into array[,] of name and averge, sort on the second item so that fastest comes first, write the results to the console with the name left-justified in a 28 character field, followed by the ticks.
And the results?
 
>timer.ly  /TIMES:1000
Sarah_ForOf                 2141.86 ticks
Sarah_SplitReverseJoin      2444.758 ticks
Sarah_Reduce                2805.243 ticks
Bruce_CharAt                2842.139 ticks
Nathanael_SplitReverseJoin  3035.17 ticks
Theophanis_SplitFor         3142.142 ticks
Bruce_Recursive1            3319.84 ticks
Bruce_Recursive2            3451.674 ticks
Theophanis_SplitFor_Bruced  3616.858 ticks
Sarah_Recursive             4645.366 ticks
Bruce_ArrayApplyMap         5637.1 ticks
Bruce_MapSortMap            9754.566 ticks
Bruce_CharAt2               13721.967 ticks


>timer.ly  /TIMES:1000 /RANDOM
Sarah_ForOf                 1850.582 ticks
Sarah_SplitReverseJoin      2655.574 ticks
Theophanis_SplitFor         2815.478 ticks
Nathanael_SplitReverseJoin  2832.566 ticks
Bruce_CharAt                2842.439 ticks
Sarah_Reduce                2845.746 ticks
Bruce_Recursive2            3224.578 ticks
Bruce_Recursive1            3306.136 ticks
Theophanis_SplitFor_Bruced  3428.827 ticks
Sarah_Recursive             4258.6 ticks
Bruce_ArrayApplyMap         5421.202 ticks
Bruce_MapSortMap            9748.012 ticks
Bruce_CharAt2               13477.231 ticks

On my computer there are 10,000,000 ticks per second (using CS.System.Diagnostics.Stopwatch.Frequency). According do the documentation "Each tick in the ElapsedTicks value represents the time interval equal to 1 second divided by the Frequency."

The bottom line? Sarah's ForOf and SplitReverseJoin are by far the fastest. Theophanis's SplitFor is also really good. That said, the differences are in microseconds or less.

NOTE: All suggestions on how to improve this testing regime gratefully received. Thanks in advance.

Please note: this blog post was originally published at Dev.to on 2019-07-20.

Wednesday, July 17, 2019

[Scraping] Do I really want to write a Google Keep tool?

After writing so many wrappers and so many scraping tools over the last few years, I'm now asking myself: "Do I really want to write tools for Google Keep?" There's been a few folk calling for a Google Keep API but nothing has materialized as yet (AFAICT.)

I do use Keep a lot as a general catchall for interesting web content. Mind you, I use Pocket as well. And PearlTrees. And Zim.

Fiddling with my Keep collection in Firefox Developer sees me trying to spot some structures that could be targeted in Selenium. One item of interest is via document.querySelectorAll(".RNfche"). At this point I don't know if the RNfche class is specific to my collection or to everyone's, but it's on every containing DIV. The number of results of the querySelectAll() increases as one scrolls down the page. Thus, to find every one of them one would have to script Selenium to scroll right down to the bottom of the collection before pulling up the list of RNfche-class elements.

Can I really be bothered?

Please note: this blog post was originally published at Dev.to on 2019-07-17.

Tuesday, July 16, 2019

[Google Apps Script] ScriptProperties Gotcha in Google Apps Script

For reasons of insanity I have wrapped the ScriptProperties of the PropertiesService in a object with get, set, forget and getKeys methods, viz:

function ScptProps() {
  this.scriptProperties = PropertiesService.getScriptProperties();
}

ScptProps.prototype.get = function (name) {
  return this.scriptProperties.getProperty(name);
};

ScptProps.prototype.set = function (name, value) {
  return this.scriptProperties.setProperty(name, value);
};

ScptProps.prototype.forget = function (name) {
  return this.scriptProperties.deleteProperty(name);
};

ScptProps.prototype.getKeys = function () {
  return this.scriptProperties.getKeys();
};

Using the REPL from my previous posting, I issued the following commands:
 
(new ScptProps).set('goose',58);
typeof (new ScptProps).get('goose');
(new ScptProps).forget('goose');

Goose is me and 58 my age for those interested.

And the gotcha? Well, I was a little taken aback recently, while debugging a number to number comparison issue, to discover that when I store a number I don't get one back. I get a string back and have to do a parseInt() on it to get its original value. The result of typeof (new ScptProps).get('goose'); is, you guessed it, string!

Please note: this blog posting was originally published in Dev.to on 2019-07-16

Thursday, July 11, 2019

[Google Apps Script] REP and almost L in Google Apps Script

It's been quite a while since I blogged about computing (I usually blog about baking) but here goes.

Lately I've been climbing a steep learning curve, trying to get my head around Google Apps Script (GAS). Now a few spreadsheets later, I'm on a trajectory that should see me crash-land on Planet Add-On in a month or two.

REPL (read-evaluate-print-loop) has been a big thing for a long time with all manner of programming languages. So why not GAS? (Okay, it's more REP than REPL as the looping doesn't happen, but it's close.)

In my Code.gs I have the following (among other things)

function onOpen() { 
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('Debugging')
  .addItem('REPL', 'REPL')
  .addToUi();  
}

This adds a custom menu to the menubar and populates it with one entry, namely 'REPL' which, when selected, runs a function called 'REPL'.
 
function REPL() {
  var code = Browser.inputBox('code');
  if (code !== 'cancel') {
    Browser.msgBox(eval(code));
  }
}

Also in there, for demonstration purposes, is a function that totals the ASCII values of the characters in the parameter string.
 
function TotalAscii(str) {
  return str.split("").reduce(function (result, item, index) {
    return result + item.charCodeAt(0)
  }, 0)
}
 
Visually there we are selecting the REPL option from the Debugging menu

 

entering something to be evaluated and getting a response.



I'd like at some stage to put together an HTML form with a TEXTAREA. Maybe after I crawl out of the crater.

Please note: This blog posting was first published at Dev.to on 2019-07-11

Friday, February 09, 2018

[JavaScript] String.prototype

Back in 2011, I started learning JavaScript. I'm still learning but haven't moved beyond ES3, largely because that's the dialect I'm using for most of my server-side scripting. In other words, Microsoft JScript (gasp, shock, horrors, gevalt etc.)

Most of the rest of what I do at the moment is using C#. A while back I went looking for a way to extend my C# programs with scripting and found
ClearScript. I've been using it to great effect for the last 3 years in the vast majority of my work projects. I've even written a JScript-on-steroids which exposes large chunks of C# to JScript and permits the writing of some very powerful scripts.

In the next few postings I'll be discussing some of the interesting things I've discovered. For a lot of folk, it'll be old hat ... so old that mice are living in it. But, who knows, maybe someone will find a use for some of it, or be able to adapt it to newer dialects.

toProperCase()

String.prototype.toProperCase = function () {
  return this.replace(/\w\S*/g, function (txt) {
    return String(txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
  });
};
I found this on StackOverflow. It attempts to proper-case (title-case) every word in a string. I don't use it often but it was an interesting find.

after
()
String.prototype.after = function (a, including) {
  including = including || false;
  a = this.indexOf(a);
  return -1 === a ? String(this) : String(this.substr(a + (including ? 0 : 1)));
};
A staple, this gives whatever is after a substring in a string. The substring can be included or excluded for the result.

before
()
String.prototype.before = function (a, including) {
  including = including || false;
  a = this.indexOf(a);
  return -1 === a ? String(this) : String(this.substr(0, a + (including ? 1 : 0)));
};
Another staple, this gives whatever is before a substring in a string. Again, the substring can be included or excluded for the result.

stripChars
()
String.prototype.stripChars = function (chars, cnt) {
  var i,
  j;
  cnt = cnt || 0;
  var answer = this;
  for (i = 0; i < chars.length; i++) {
    var chr = chars.charAt(i);
    if (cnt === 0) {
      var pat = new RegExp(chr, "g");
      answer = answer.replace(pat, "");
    } else {
      for (j = 0; j < cnt; j++) {
        answer = answer.replace(chr, "");
      }
    }
  }
  return String(answer);
};
I don't use this often, but it has been useful. It removes each element of chars from the string. If cnt is undefined or zero, all are removed. If cnt is 1 or more, then that many are removed.

between
()
String.prototype.between = function (begin, end, including) {
  including = including || false;
  var left = this.indexOf(begin);
  if (left < 0) {
    return String('');
  }
  left += begin.length;
  var right = typeof end === 'undefined' ? this.length : this.indexOf(end, left);
  if (right < 0) {
    return String('');
  }
  return including ? String(begin + this.substring(left, right) + end) : String(this.substring(left, right));
};
There are various ways of pulling SGML and other markups apart. This one, in various languages, has been in the toolkit for years. This variant permits excluding or including the delimiters. Apart from that, it isn't smart -- "drooling singing".between("droo","ing") gives l

endswith
()
String.prototype.endswith = function (str) {
  return this.indexOf(str) !== -1 && this.indexOf(str) === this.length - str.length;
};
Returns true if the strings ends with contents of str otherwise false. Has helped with detecting filetypes in filenames.

toSqlString
()
String.prototype.toSqlString = function () {
  return String("'" + String(this).replace(/'/g, "''") + "'");
};
This puts a single quote at the start and end of the string and doubles any embedded single quotes. I use this in combination with .questionMarkIs() to format SQL statements.

toArrayOfLines
String.prototype.toArrayOfLines = function () {
  return this.split(/\r\n|\r|\n/g);
};
Splitting a text file to lines is something I do a lot of. This is the functional variant of code I have in almost every script written over the last 7 years.

questionMarkIs

String.prototype.questionMarkIs = function (val) {
  return String(this.replace("?", val));
};
For a while I was doing all kinds of weird things to build SQL queries. Lately I've taken to simply putting question marks in and then replacing them with the value (with or without .toSqlString()).

That should do for now. Enjoy!

© Copyright Bruce M. Axtens, 2018

Thursday, September 17, 2015

[JavaScript] SQLServer Date to jsDateTime

I forgot to include in the last posting the reverse function that takes a SQLServer Epoch value and converts it to a JavaScript datetime number.
function SQLServerDateTojsDateTime(a) {
  return Math.ceil((a * 86400 - 2208988800) * 1000);
 }
An example of its use (from a jscli session):
> d = new Date()
 Thu Sep 17 12:03:58 UTC+0800 2015
 > d.valueOf()
 1442462638627
 > n = jsDateTimeToSQLServerDateTime(d)
 42262.169428553236
 > m = SQLServerDateTojsDateTime(n)
 1442462638627
 > new Date(m)
 Thu Sep 17 12:03:58 UTC+0800 2015
Enjoy!

© Copyright Bruce M. Axtens, 2015

Wednesday, September 16, 2015

[JavaScript] Converting JavaScript DateTime to SQLServer DateTime

I've had the need occasionally to convert JavaScript datetime to SQLServer datetime numeric Epoch value. The following two routines do the job. In both cases I'm assuming that UTC is being used as taking timezones into consideration is a bit more work.

First jsDateTimeToSQLServerDate which takes a JavaScript Date object and forms a SQLServer Epoch integer.

   function jsDateTimeToSQLServerDate(d) {

      var SQLServerEpoch = 2208988800;
      var dMilli = d.valueOf();
      var dSec = dMilli / 1000;
      var nEpoch = SQLServerEpoch + dSec;
      var nEpochDays = nEpoch / 86400;
      var nEpochDate = Math.ceil(nEpochDays);
      return nEpochDate;
    }
The second also takes a JavaScript Date object but this time includes the time returning an Epoch floating point number.
   function jsDateTimeToSQLServerDateTime(d) {
      var SQLServerEpoch = 2208988800;
      var dMilli = d.valueOf();
      var dSec = dMilli / 1000;
      var nEpoch = SQLServerEpoch + dSec;
      var nEpochDays = nEpoch / 86400;
      var nEpochDate = Math.ceil(nEpochDays);
      return nEpochDays;
    }
The Closure compiler abbreviates those routines signficantly, as below. You may want to use this code in preference as the above was done to demonstrate the relationships in the conversion process.
   function jsDateTimeToSQLServerDate(a) {
      return Math.ceil((2208988800 + a.valueOf() / 1E3) / 86400);
    }
    function jsDateTimeToSQLServerDateTime(a) {
      a = (2208988800 + a.valueOf() / 1E3) / 86400;
      Math.ceil(a);
      return a;
    }
    ;
An example invocation:
   var now = new Date();
    // now currently
    // Wed Sep 16 22:52:09 UTC+0800 2015
    jsDateTimeToSQLServerDate(now);
    // gives
    // 42262
    jsDateTimeToSQLServerDateTime(now);
    // gives
    // 42261.61955061343
I hope this helps. I may be back here myself next time I need this routine.

© Copyright Bruce M. Axtens, 2015

Wednesday, September 11, 2013

[Open Source] Github and Bitbucket

The blog's been very quiet lately. Too quiet. But I've been busy.

I got started on
Github, but the only thing I have there is a failed attempt at building a JSON library for the Euphoria programming language.

Most of the more recent stuff is happening on Bitbucket where I have various implementations (javascript v1 and javascript v2 (minimal), euphoria, vb6, php, and c#) of Steve Skiena's integer bignums library and a few other bits and bobs, namely:


All projects are in varying stages of completeness, and looking for users and improvers.


© Copyright Bruce M. Axtens, 2013

Saturday, June 16, 2012

[JScript] The name of the current function

This is a plug for StackOverflow, where I spend a fair bit of time.

Today I went looking for Javascript code to give me the name of the currently executing function. I found answers
here and here. And now I have the following in my script:

var fname = arguments.callee.toString().split(/ /)[1].split(/\(/)[0];

Woe betide if callee is ever disabled!


© Copyright Bruce M. Axtens, 2012

Sunday, December 19, 2010

[Javascript] Wildcard string matching / globbing, Take 2.

Okay, I'm convinced: you just can't beat regular expressions. (Okay, SNOBOL4's pattern matching is streets ahead, but there's nothing out there like that for Javascript, AFAIK.)

I've reworked the matchesWild function, caling it grepWild. It takes the wildCard parameter as before but instead of all the substr stuff, it replaces '?' with '.', '*' with '.*' and wraps with '^' and '$'. Then it feeds that into match().

It's simpler, easier on the eyes, and faster too.
© Bruce M. Axtens, 2010

Tuesday, November 30, 2010

[Javascript] Wildcard string matching / globbing

Most Javascript programmers are more than happy with regular expressions. However, there are times when something like wildcards ('?' for one character and '*' for many) would be helpful.

Jack Handy's first and last (it seems) article on CodeProject implemented a wildcard string compare (also known as 'globbing') function in C. Below I present my port of that function to Javascript.

As a newbie Javascript programmer (4 weeks in as of 2010-11-30), I've pleasantly surprised by the expressive power of Javascript. What has caught my eye recently is the power of .prototype., so this implementation of matchesWild() is declared as a prototype extending the String class.

Here's the code, with some commentary:
Apart from the comments, notice the way Javascript adds a method to an object, defining an anonymous function with, in this case, one parameter, and assigning it as a prototype.
The C version did all kinds of interesting things with pointers. Javascript doesn't have that kind of stuff (AFAICT), so I had to do the equivalent with numeric offsets and substr().

Perhaps there are better ways of doing this. I have tried to do this kind of thing before, but this code seems to do a pretty good job of it and hasn't failed thus far.

What should be noted, and perhaps dealt with, is that there is no explicit check for calling the method without a parameter. If one does this, an error is raised. Chrome's V8 raises "TypeError: Cannot call method 'substr' of undefined".
And now some examples (evaluated using macports's JavaScript-C 1.7.0 2007-10-03)
Outputs:
More Javascript coming soon. In the meantime, enjoy.

© Bruce M. Axtens, 2010