Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the fastest implementation of ColdFusion's listFindNoCase function in Javascript?

I've grown spoiled by ColdFusion's lists, and have run across a situation or two where a comma delimited list shows up in Javascript. Is there an equivalent of listFindNoCase('string','list'), or a performant way to implement it in Javascript?

Oh, and it should be able to handle list items with commas, like: ( "Smith, John" , "Doe, Jane" , "etc..." )

That's what is really tripping me up.

like image 371
eterps Avatar asked Jul 07 '12 00:07

eterps


3 Answers

FYI: jList's implementation: https://github.com/davidwaterston/jList

Although, this will fail your requirement that "it should be able to handle list items with commas"

listFind : function (list, value, delimiter) {
    delimiter = (typeof delimiter === "undefined") ? "," : delimiter;

    var i,
        arr = list.split(delimiter);

    if (arr.indexOf !== undefined) {
        return arr.indexOf(value) + 1;
    }

    for (i = 0; i < list.length; i += 1) {
        if (arr[i] === value) {
            return i + 1;
        }
    }

    return 0;
},


listFindNoCase : function (list, value, delimiter) {
    delimiter = (typeof delimiter === "undefined") ? "," : delimiter;

    list = list.toUpperCase();
    value = String(value).toUpperCase();

    return this.listFind(list, value, delimiter);
},
like image 131
Henry Avatar answered Nov 10 '22 16:11

Henry


One relevant observation here is that CF lists themselves don't support the delimiter char also being part of the data. Your sample "list" of '"Smith, John", "Doe, Jane"' is a four-element comma-delimited list of '"Smith', 'John"', '"Doe', 'Jane"'. To fulfil your requirement here you don't want a JS equiv of CF's listFindNoCase(), because listFindNoCase() does not actually fulfill your requirement in from the CF perspective, and nothing native to CF does. To handle elements that have embedded commas, you need to use a different char as a delimiter.

TBH, CF lists are a bit rubbish (for the reason cited above), as they're only really useful in very mundane situations, which a) don't often crop up; b) aren't better served via an array anyhow. One observation to make here is you are asking about a performant solution here: not using string-based lists would be the first step in being performant (this applies equally to CF as it does to JS: CF's string-based lists are not at all performant).

So my first answer here would be: I think you ought to revise your requirement away from using lists, and look to use arrays instead.

With that in mind, how is the data getting to JS? Are you some how stuck with using a string-based list? If not: simply don't. If your source data is a string-based list, are you in the position to convert it to an array first? You're in trouble with the "schema" of your example list as I mentioned before: from CF's perspective you can't have a comma being both a delimiter and data. And you do have a bit of work ahead of you writing code to identify that a quoted comma is data, and a non-quoted comma is a delimiter. You should have a look around at CSV-parsing algorithms to deal with that sort of thing.

However if you can change the delimiter (to say a pipe or a semi-colon or something that will not show up in the data), then it's easy enough to turn that into an array (listToArray() in CF, or split() in JS). Then you can just use indexOf() as others have said.

For the sake of sh!ts 'n' giggles, if you were stuck with a string - provided you could change the delimiter - you could do this, I think:

  1. use indexOf() to find the position of the first match of the substring in the string, you will need to use a regex to match the substring which is delimited by your delimiter char, or from the beginning of the string to a delimiter char, or from a delimiter char to the end of the string with no intermediary delimiter chars. I could come up with the regex for this if needs must. This will not be list-aware yet, but we know where it'll be in the string.
  2. Take a substring of the original string from the beginning to the position indexOf() returned.
  3. Use split() on that, splitting on the delimiter
  4. The length of the ensuing array will be the position in the original list that the match was at.

However I stress you should not do that. Use an array instead of a string from the outset.

like image 21
Adam Cameron Avatar answered Nov 10 '22 16:11

Adam Cameron


You can use indexOf combined with .toLowerCase()

var list = '"Smith, John" , "Doe, Jane" , "etc..."';
if(list.toLowerCase().indexOf('"Smith, John"'))

If you need an exact match, like "Smith" when "Smithson" exists, just pad the strings with your delimiter. For example, let's say your delimiter is a semi-colon (because you have commas in your string), pad the left and right sides of your string like so:

";Smith, John;Doe, Jane;"

Also pad the search value, so if you're looking for Smith, the value would become:

";Smith;"

.toLowerCase().indexOf() would return -1 (not found). But ";Smith, John;" would return 0

like image 31
Chris Gessler Avatar answered Nov 10 '22 17:11

Chris Gessler