83

I am making a piece of code for a website that will have a list of names in an array and pick a random name, I want to add a feature that will let the user add or delete a name from the array. I have all of these features but when deleting a name, the user has to type the name to match the Case in the array. I tried to make the so it would be Case-Insensitive, what am I doing wrong?

<html>
<!--Other code uneeded for this question-->
<p id="canidates"></p>
<body>
<input type="text" id="delname" /><button onclick="delName()">Remove Name from List</button>
<script>

//Array of names

var names = [];

//Other code uneeded for this question

//List of Canidates
document.getElementById('canidates').innerHTML = 
"<strong>List of Canidates:</strong> " + names.join(" | ");

//Other code uneeded for this question

//Remove name from Array

function delName() {
    var dnameVal = document.getElementById('delname').value;
    var pos = names.indexOf(dnameVal);
    var namepos = names[pos]
    var posstr = namepos.toUpperCase();
    var dup = dnameVal.toUpperCase();
    if(dup != posstr) {
        alert("Not a valid name");
        }
    else {
        names.splice(pos, 1);
        document.getElementById('canidates').innerHTML = 
        "<strong>List of Canidates:</strong> " + names.join(" | ");
        }
    }   
</script>
</body>
</html>
Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
Malcolm
  • 841
  • 1
  • 6
  • 6
  • 3
    You can't make `.indexOf()` case insensitive. You can normalize the case between the Array and the search term. You seem to be doing a search and then converting the case, so that isn't going to help you. You could do `.toUpperCase()` on the `dnameVal` before the search, but that would only work if all the names are also upper case. – cookie monster Jul 13 '14 at 00:01
  • 3
    The question that was linked to as the duplicate is about a case-insensitive search in a string, while this one is about a case-insensitive search in an array. – Robby Cornelissen Jul 13 '14 at 00:14
  • 3
    In this question, the OP is using `Array.indexOf`, the answer that is pointed to (and why closed) is using `String.indexOf` – Xotic750 Jul 13 '14 at 00:15

13 Answers13

82

ES2015 findIndex:

var array = ['I', 'hAve', 'theSe', 'ITEMs'],
    indexOf = (arr, q) => arr.findIndex(item => q.toLowerCase() === item.toLowerCase());

console.log(  indexOf(array, 'i')      ) // 0
console.log(  indexOf(array, 'these')  ) // 2
console.log(  indexOf(array, 'items')  ) // 3
vsync
  • 118,978
  • 58
  • 307
  • 400
  • 10
    Not supported by Internet Explorer. – V Setyawan Apr 11 '17 at 17:12
  • 1
    @Vznz - if you're using *Babel* or something similar it will *transpile* it automatically, it is a very common practice these days. – vsync Feb 17 '20 at 19:53
  • 4
    @VSetyawan who cares? that browser should long been gone! – eddy Sep 09 '20 at 00:55
  • 2
    @VSetyawan Lol, what is though ¯\_(ツ)_/¯ – Jim Jam Jun 07 '21 at 15:58
  • @eddy, shame your personal opinion of what should or shouldn't be has zero bearing on reality. There are companies and organizations still using **DOS** for any of numerous different reasons; IE is hardly that unusual. – Synetech Mar 18 '23 at 21:04
33

In ECMA-262, 5th edition, you could use Array.prototype.some for this.

var array = [ 'I', 'hAve', 'theSe', 'ITEMs' ];
var query = 'these'.toLowerCase();
var index = -1;
array.some(function(element, i) {
    if (query === element.toLowerCase()) {
        index = i;
        return true;
    }
});
// Result: index = 2
quietmint
  • 13,885
  • 6
  • 48
  • 73
  • 1
    This is the best answer if you change `query` to `query.toLowerCase()` in your `if`. – Shelby L Morris Jul 13 '14 at 01:37
  • 3
    @sxnine And likewise, you would actually use `String(element).toLowerCase()` in case `element` wasn't guaranteed to be a string... I assumed the `query` was already in lower case. I edited the post to add it outside the loop for demonstration purposes. It doesn't belong inside the loop because then it would happen for each iteration. – quietmint Jul 13 '14 at 02:23
30

Easy way would be to have a temporary array that contains all the names in uppercase. Then you can compare the user input. So your code could become somthing like this:

function delName() {
    var dnameVal = document.getElementById('delname').value;
    var upperCaseNames = names.map(function(value) {
      return value.toUpperCase();
    });
    var pos = upperCaseNames.indexOf(dnameVal.toUpperCase());

    if(pos === -1) {
        alert("Not a valid name");
        }
    else {
        names.splice(pos, 1);
        document.getElementById('canidates').innerHTML = 
        "<strong>List of Canidates:</strong> " + names.join(" | ");
        }
    }

Hope this helps solve your problem.

Ralph
  • 407
  • 4
  • 5
  • 2
    Everyone is seriously overthinking this with their custom indexOf implementation...Although I liked user113215 answer of using **Array.prototype.some**... – Ralph Jul 13 '14 at 10:23
  • Why even create an array which is all in upper case? Why not just convert the array directly to something which is already case-comparable? Like a string? – DMCoding May 10 '16 at 12:32
13

The most elegant solution would be to convert the array into a string first, then do a case insensitive comparison. For example:

var needle = "PearS"
var haystack = ["Apple", "banNnas", "pEArs"];
var stricmp = haystack.toString().toLowerCase(); // returns 
                                   // "apple,bananas,pears"
if (stricmp.indexOf(needle.toLowerCase()) > -1) {
    // the search term was found in the array
} else {
    // the search term was not found in the array
}
DMCoding
  • 1,167
  • 2
  • 15
  • 30
5

Probably best to create your own custom indexOf method, something like this.

'use strict';

var customIndexOf = function(arrayLike, searchElement) {
  var object = Object(arrayLike);
  var length = object.length >>> 0;
  var fromIndex = arguments.length > 2 ? arguments[2] >> 0 : 0;
  if (length < 1 || typeof searchElement !== 'string' || fromIndex >= length) {
    return -1;
  }

  if (fromIndex < 0) {
    fromIndex = Math.max(length - Math.abs(fromIndex), 0);
  }

  var search = searchElement.toLowerCase();
  for (var index = fromIndex; index < length; index += 1) {
    if (index in object) {
      var item = object[index];
      if (typeof item === 'string' && search === item.toLowerCase()) {
        return index;
      }
    }
  }

  return -1;
};

var names = [
  'John',
  'Anne',
  'Brian'
];

console.log(customIndexOf(names, 'aNnE'));

Or even

'use strict';

var customIndexOf = function(array, searchElement, fromIndex) {
  return array.map(function(value) {
    return value.toLowerCase();
  }).indexOf(searchElement.toLowerCase(), fromIndex);
};

var names = [
  'John',
  'Anne',
  'Brian'
];

console.log(customIndexOf(names, 'aNnE'));

You may also want to add more checks to be sure that each element in the array is actually a String and that the searchElement is also actually a String too. If pre-ES5 then load appropriate shims

Xotic750
  • 22,914
  • 8
  • 57
  • 79
3

You can use Array.prototype.find()

found = myArray.find(key => key.toUpperCase() === searchString.toUpperCase()) != undefined;

Example:

myArray = ['An', 'aRRay', 'oF', 'StringS'];
searchString = 'array';
found = myArray.find(key => key.toUpperCase() === searchString.toUpperCase()) != undefined;
if (found ) {
    // The array contains the search string
}
else {
    // Search string not found
}

Note: Array cannot contain undefined as a value.

Community
  • 1
  • 1
ChickenFeet
  • 2,653
  • 22
  • 26
1

It is possible using by map method. For example see below code

var _name = ['prasho','abraham','sam','anna']
var _list = [{name:'prasho'},{name:'Gorge'}];

for(var i=0;i<_list.length;i++)
{
   if(_name.map(function (c) {
     return c.toLowerCase();
   }).indexOf(_list[i].name.toLowerCase()) != -1) { 
  //do what ever
   }else{
     //do what ever
   }
}

More info

Prashobh
  • 9,216
  • 15
  • 61
  • 91
1

I needed something similar to this where I needed compare two strings using includes and needed to be able to support both case and case insensitive searches so I wrote the following small function

function compare(l1: string, l2: string, ignoreCase = true): boolean {
  return (ignoreCase ? l1.toLowerCase() : l1).includes((ignoreCase ? l2.toLowerCase() : l2));
}

Same principle could apply to indexOf as below

function indexOf(l1: string, l2: string, ignoreCase = true): number {
  return (ignoreCase ? l1.toLowerCase() : l1).indexOf((ignoreCase ? l2.toLowerCase() : l2));
}

I know this is not specifically Array.indexOf but hope this helps someone out if the come across this post on their travels.

To answer the ops question though, you can apply this similarly to an array combined with this answer from @ULIT JAIDEE (the slight change to this was using the tilda character as a separator in case any of the array values contained spaces)

function compare(l1: any[], l2: any[], ignoreCase = true): boolean {
  return (ignoreCase ? l1.join('~').toLowerCase().split('~') : l1).indexOf((ignoreCase ? l2.join('~').toLowerCase().split('~') : l2));
}

Again hope this helps.

Neil Stevens
  • 3,534
  • 6
  • 42
  • 71
0

Turn the array into a string separated by a delimiter, turn that string lowercase, and then split the string back into an array by the same delimiter:

function findIt(arr, find, del) {
  if (!del) { del = '_//_'; }
  arr = arr.join(del).toLowerCase().split(del);
  return arr.indexOf(find.toLowerCase());
}

var arr = ['Tom Riddle', 'Ron Weasley', 'Harry Potter', 'Hermione Granger'];
var find = 'HaRrY PoTtEr';
var index = findIt(arr, find);

if (~index) {
  alert('Found ' + arr[index] + '! :D');
} else {
  alert('Did not find it. D:');
}
Kodie Grantham
  • 1,963
  • 2
  • 17
  • 27
0

This is the shortest one.

haystack.join(' ').toLowerCase().split(' ').indexOf(needle.toLowerCase())
0
// unique only, removes latter occurrences    
array.filter((v, i, a) => a.findIndex(j => v.toLowerCase() === j.toLowerCase()) === i);
colidyre
  • 4,170
  • 12
  • 37
  • 53
vcz
  • 1
0

To improve on @vsync answer and handle mixed content in the array here is my take. (I understand the OP is about case-sensitive thus it implies strings, maybe :)

var array = ['I', 'hAve', 7, {}, 'theSe', 'ITEMs'],
  Contains = (arr, q) =>
    arr.findIndex((item) => q.toString().toLowerCase() === item.toString().toLowerCase());

console.log(Contains(array, 'i'));
console.log(Contains(array, 'x'));
console.log(Contains(array, {} ));
console.log(Contains(array, 7 ));

Meryan
  • 1,285
  • 12
  • 25
-2

You can't make it case-insensitive. I'd use an object instead to hold a set of names:

function Names() {
    this.names = {};

    this.getKey = function(name) {
        return name.toLowerCase();
    }

    this.add = function(name) {
        this.names[this.getKey(name)] = name;
    }

    this.remove = function(name) {
        var key = this.getKey(name);

        if (key in this.names) {
            delete this.names[key];
        } else {
            throw Error('Name does not exist');
        }
    }

    this.toString = function() {
        var names = [];

        for (var key in this.names) {
            names.push(this.names[key]);
        }

        return names.join(' | ');
    }
}

var names = new Names();

function update() {
    document.getElementById('canidates').innerHTML = '<strong>List of Canidates:</strong> ' + names;
}

function deleteName() {
    var name = document.getElementById('delname').value;

    try {
        names.remove(name);
        update();
    } catch {
        alert('Not a valid name');
    }
}

update();
Blender
  • 289,723
  • 53
  • 439
  • 496