Tag Archives: javascript

What is the index of an empty string in an empty string?

This sounds like a question a programmer might ask after one medicinal cigarette too many. The computer science equivalent of “what is the sounds of one hand clapping?”. But it is a question I have to decide the answer to.

I am adding indexOf() and lastIndexOf() operations to the Calculate transform of my data wrangling (ETL) software (Easy Data Transform). This will allow users to find the offset of one string inside another, counting from the start or the end of the string. Easy Data Transform is written in C++ and uses the Qt QString class for strings. There are indexOf() and lastIndexOf() methods for QString, so I thought this would be an easy job to wrap that functionality. Maybe 15 minutes to program it, write a test case and document it.

Obviously it wasn’t that easy, otherwise I couldn’t be writing this blog post.

First of all, what is the index of “a” in “abc”? 0, obviously. QString( “abc” ).indexOf( “a” ) returns 0. Duh. Well only if you are a (non-Fortran) programmer. Ask a non-programmer (such as my wife) and they will say: 1, obviously. It is the first character. Duh. Excel FIND( “a”, “abc” ) returns 1.

Ok, most of my customers, aren’t programmers. I can use 1 based indexing.

But then things get more tricky.

What is the index of an empty string in “abc”? 1 maybe, using 1-based indexing or maybe empty is not a valid value to pass.

What is the index of an empty string in an empty string? Hmm. I guess the empty string does contain an empty string, but at what index? 1 maybe, using 1-based indexing, except there isn’t a first position in the string. Again, maybe empty is not a valid value to pass.

I looked at the Qt C++ QString, Javascript string and Excel FIND() function for answers. But they each give different answers and some of them aren’t even internally consistent. This is a simple comparison of the first index or last index of text v1 in text v2 in each (Excel doesn’t have an equivalent of lastIndexOf() that I am aware of):

Changing these to make the all the valid results 1-based and setting invalid results to -1, for easy comparison:

So:

  • Javascript disagrees with C++ QString and Excel on whether the first index of an empty string in an empty string is valid.
  • Javascript disagrees with C++ QString on whether the last index of an empty string in a non-empty string is the index of the last character or 1 after the last character.
  • C++ QString thinks the first index of an empty string in an empty string is the first character, but the last index of an empty string in an empty string is invalid.

It seems surprisingly difficult to come up with something intuitive and consistent! I think I am probably going to return an error message if either or both values are empty. This seems to me to be the only unambiguous and consistent approach.

I could return a 0 for a non-match or when one or both values are empty, but I think it is important to return different results in these 2 different cases. Also, not found and invalid feel qualitatively different to a calculated index to me, so shouldn’t be just another number. What do you think?

*** Update 14-Dec-2023 ***

I’ve been around the houses a bit more following feedback on this blog, the Easy Data Transform forum and hacker news and this what I have decided:

IndexOf() v1 in v2:

v1v2IndexOf(v1,v2)
1
aba
aba1
aa1
aaba1
xy
worldhello world7

This is the same as Excel FIND() and differs from Javascript indexOf() (ignoring the difference in 0 or 1 based indexing) only for “”.indexOf(“”) which returns -1 in Javascript.

LastIndexOf() v1 in v2:

v1v2LastIndexOf(v1,v2)
1
aba
aba4
aa1
aaba3
xy
worldhello world7

This differs from Javascript lastIndexOf() (ignoring difference in 0 or 1 based indexing) only for “”.indexOf(“”) which returns -1 in Javascript.

Conceptually the index is the 1-based index of the first (IndexOf) or last (LastIndexOf) position where, if the V1 is removed from the found position, it would have to be re-inserted in order to revert to V2. Thanks to layer8 on Hacker News for clarifying this.

Javascript and C++ QString return an integer and both use -1 as a placeholder value. But Easy Data Transform is returning a string (that can be interpreted as a number, depending on the transform) so we aren’t bound to using a numeric value. So I have left it blank where there is no valid result.

Now I’ve spent enough time down this rabbit hole and need to get on with something else! If you don’t like it you can always add an If with Calculate or use a Javascript transform to get the result you prefer.

*** Update 15-Dec-2023 ***

Quite a bit of debate on this topic on Hacker News.

Programming skills wanted

I am looking to outsource some self-contained programming tasks in areas that I don’t have expertise in. I am hoping that someone reading this blog might be able to help (or know someone that can) so I don’t have to go through outsourcing sites. These are the two skills sets I am currently looking for:

  1. Javascript/CSS/HTML – To write a single page web app. This will have a relatively simple UI displaying data read from XML. The app will need to work on a wide range of browsers and devices. Ideally you should also have some web design skills, but this isn’t essential.
  2. C++/Qt 4/OpenGL – To write a relatively simple 3D visualization model that runs on Windows and Mac. This will involve populating a 3D space with specified shapes and allowing simple movement around it.

Details:

  • I am expecting that I will need 2 different people, but it is possible there might be someone out there with experience in both.
  • These are small projects (probably less than 2 weeks for task 1 and less than 1 week for task 2). But they might lead on to more work in future.
  • Time scales are reasonably relaxed. Ideally I would like the work to be finished by the end of September.
  • You can be based anywhere in the world, but must be able to communicate in English (written and spoken).
  • Full copyright to the work will pass to my company on full payment.
  • Obviously cost is an issue. If I have 2 promising candidates, I am likely to pick the cheaper one.

If you are interested in doing either of these tasks please email me ( andy at oryxdigital.com ) before the end of Friday 26th August with subject “programming work” and a brief outline of:

  • Which of the 2 tasks you are interested in.
  • Your relevant experience. Ideally including details of related projects completed.
  • Your daily rate in Pounds Sterlings or US dollars.

I will send detailed specs to a shortlist of the best candidates. The work will be awarded on the basis of fixed price bids against the spec. Please don’t apply unless you have relevant experience – if I wanted a programmer without experience in these areas I could do it myself. ;0)

A new front-end for e-junkie

e-junkieI am been very happy using e-junkie as my payment processor for the last 4 years. I pay them a few dollars per month as a flat-fee and they provide an interface to PayPal, GoogleCheckout and 2Checkout (and others) plus additional features such as sending licence keys and handling coupon codes. It isn’t a fully fledged a registration service like Avangate, FastSpring or Plimus, but it has been adequate for my needs, has responsive customer support and is very cheap. In theory I could have written a load of scripts to do what e-junkie does, but re-inventing that wheel would be a lousy use of my valuable time.

I had been using e-junkie ‘Buy now’ buttons, but things were starting to get complicated with the branching of my single PerfectTablePlan product into three products: PerfectTablePlan Home Edition, PerfectTablePlan Advanced Edition and PerfectTablePlan Professional Edition.  3 products, each with and without a CD and available in 3 currencies is 18 purchase options, not including the choice of PayPal, GoogleCheckout and 2Checkout as processor. I also had additional options for upgrading version (e.g. v3->v4) and upgrading edition (e.g. Home->Professional). Doing all this through ‘buy now’ buttons was clearly going to be a mess.

I looked at the e-junkie shopping cart, but it had a number of shortcomings I couldn’t live with, most notably:

  • It always shows a coupon field. I don’t use coupons very often. A coupon field says to a customer without a coupon “Someone else getting this cheaper than you – sucker!”. There is a good chance that they will hit the back button and start searching for a coupon (I have done it myself). Maybe they will never come back. I only want the coupon field to appear if I give the user a particular URL, and I will only give that URL to customers who have coupon codes.
  • It always shows the ‘Buy with GoogleCheckout’ button – even if GoogleCheckout don’t do transactions in that currency. So a customer buying in US Dollars can click the ‘Buy with GoogleCheckout’ button, only to be told they can’t buy in dollars through GoogleCheckout. That is a very poor customer experience.

I investigated e-junkie ‘variants’, but these weren’t an adequate solution either. I was loathe to pay more for my payment processing. So I asked my good friend Paul Kossowski, an experienced Javascript programmer, to write me a payment form front-end to e-junkie. My basic requirements were:

  1. Handle multiple products, options and currencies.
  2. Show a running total depending on the product, number and options selected.
  3. Default to a sensible currency, based on the customer’s location from the free maxmind.com geolocation service.
  4. Mustn’t hang if the geolocation service is down.
  5. Make it easy to change prices and product descriptions.
  6. Make it easy to configure options, currencies etc (e.g. GoogleCheckout only allows me to charge in pounds sterling).
  7. Make it easy to change the ‘look and feel’ of the form.
  8. Only show a coupon field if passed an appropriate argument in the URL.
  9. Allow me access to the Javascript source so I can make it call the appropriate e-junkie URL, pass cookies and make other tweaks.

I am very pleased with the results. Here is a screenshot (click to enlarge):

payment form

click to enlarge

You can play with test versions that link through to PayPal, GoogleCheckout and 2Checkout via e-junkie by clicking on the links below (I prefer you didn’t play with my live payment pages, thanks):

Note that some links are broken on these test pages, but the link to the ecommerce providers are live. So don’t type in your credit card number, unless you are feeling generous.

The HTML on these pages includes some simple Javascript that sets up some arrays with the various products, prices, currencies etc. This then calls a separate (obfuscated) .js file which does the real work. An example of the set-up code is shown below:

form_javascript_example

click to enlarge

The look and feel of the form is controlled by a .css file. The resulting form looks fairly trivial on the page, but the .js file actually runs to several hundred lines of Javascript and took a few days for Paul to write and test, partly because of all the configuration options.

I think Paul’s form + e-junkie makes for a very professional looking and flexible payment solution at a very low cost. If you are interested in having Paul customize the Javascript for use on your site you can email him at: paul (at) dolphinfutures (dot) com . Expect to pay for a day or more of his time at UK rates, depending on your requirements.

Cookie tracking for profit and pleasure

It is great to make sales. But you really need to know where these sales are coming from to optimise your marketing. A simple and effective way to do this is through cookie tracking. The basic process is:

  • A visitor arrives at a web page on your site.
  • A script on your web page stores a small file (cookie) on their computer with some tracking details, e.g. the web page they came from, the date they arrived and the page they arrived at.
  • As they navigate to other pages the Javascript on these pages recognises that the cookie already exists and doesn’t modify it.
  • When (if) the visitor makes a purchase, the contents of the cookie are sent through to your payment provider.
  • Your payment provider sends back the cookie data with all the other information about the sale.

From the referrer you can find out what your customer typed into a search engine to find you. For example if the referrer is:

http://www.google.com/search?hl=en&q=backup software

You can infer that the purchaser found you by typing “backup software” into Google. This is incredibly useful information. Once you have amassed enough of it you can find out which keywords are most effective at selling your product. For example, whether “back-up software” makes more sales than “backup software” or “back-up programs”. This can be very helpful for fine-tuning your marketing message, SEO and PPC campaigns. You can also find out which websites purchasers are being referred from, and even how long purchasers take to make a sale after first arriving at your site.

You can get a lot of this information from Google Adwords conversion tracking. But you will only get data on sales through Adwords. I want data on all my sales. You can also get some of this information through Google Analytics. But you can only get the information in the form that Analytics wants you to have it and the price is allowing Google to see all this data as well. So I think it is well worth doing your own tracking, even if you are using Adwords conversion tracking and Analytics.

If you do use tracking cookies you will find that there is no cookie data for many transactions or the cookie data is unreliable. Reasons for this include:

  1. The cookie has expired before the customer made the purchase.
  2. The cookie has been pushed out of the cache by other cookies. Browsers only have a limited cookie cache, and your cookie might be pushed out of the cache by others long before any expiration date you set.
  3. A different person is buying the software to the person who first arrived at your site.
  4. A different computer or browser is used to buy the software to the one use to find the site.
  5. The customer clicked a button in your desktop software (not a browser) to go to your site, so there is no referrer information.
  6. A firewall or other software is blocking cookies.
  7. The customer has disabled JavaScript in their browser.

So cookie tracking data is never going to be particularly reliable. My own data shows that about 30% of sales don’t return cookie data. But it is likely to be considerably worse for B2B sales due to the longer sales cycles and the increased likelihood of the buyer not be being the person who first found the product.

With these caveats in mind, I think it is worth the time to set up cookie tracking. It is pretty quick and easy to do. You can even use the free JavaScript published at www.webmarketingplus.co.uk. Note the conditions of use. Note also what an ugly language JavaScript is[1]. I recommend placing the JavaScript in a single file which you include in each page, so you only have a single place to make modifications, for example:

<script language=“JavaScript” type=“text/javascript” src=“refercookie.js”> </script>

Sending the contents of the cookie to your payment provider is also quite straightforward. For example, for e-junkie I just use some JavaScript to extract the cookie contents and append:

&custom=<cookie contents>

to the end of the ‘Buy now’ button URL e-junkie gives you. The cookie data then comes back to me in the ‘custom:’ field of the e-junkie sale confirmation email (I believe all the major e-commerce providers support something similar). I then store the cookie data along with all the other sales data. I can use this data to generate various graphs and reports, including top-selling keywords and a graph of the time-taken to purchase. Unlike much of the data you get from Analytics this is data you can really use, e.g. for the top selling keywords:

  • Make sure they are in your Adwords campaign.
  • Write additional content pages based around these keywords to attract targeted traffic.
  • Consider including these keywords in the strapline on your home page.

The use of cookies does have privacy implications, but these are often overstated. In theory all the information in a cookie could be retrieved from server log files, cookies are just a more convenient way or doing it. Users can also disable cookies in their browser settings or using other software. I think it is fine to use cookies as long as you make this clear to your visitors. You should still have a clearly stated privacy policy for your website and this should contain a brief description of what information you are storing in cookies.

Knowing a bit about cookies can also help you as a consumer. A while back I was interested in buying a large VDU from Dell. I browsed around their site and found a good deal. I went back some time later to buy the monitor after I had bought a new PC, but the price had gone up considerably. On a hunch I deleted Dell’s cookie and refreshed the page. The price dropped back to the original price. I believe that Dell knew from a cookie that:

  1. I had logged in as a business user; and
  2. Had just purchased a new PC from Dell.

Consequently they expected me to be less price sensitive than a consumer shopping for just a VDU and upped the price. I can’t prove this. It is also possible (but unlikely) that they just happened to drop the price in the few seconds before I did a refresh. Anyway, try it next time you want to buy something expensive online. Note that it might be easier to use another browser (e.g. Opera or Safari) than to delete cookies. Let me know if you get a similar result.

[1] It has been said that JavaScript bears as much resemblance to Java as the Taj Mahal Indian restaurant bears to the Taj Mahal. And Java is hardly a ‘looker’.