2

Assuming I have an array of words and a few camelCase strings as follows:

var arr = ["hello", "have", "a", "good", "day", "stackoverflow"];
var str1 = "whenTheDayAndNightCollides";
var str2 = "HaveAGoodDay";
var str3 = "itIsAwfullyColdDayToday";
var str4 = "HelloStackoverflow";

How would I split the camelCase words into individual strings, compare each split string (converted to lowercase) to the arr array elements and return true if every split string is part of the specified array?

"whenTheDayAndNightCollides" // should return false since only the word "day" is in the array

"HaveAGoodDay" // should return true since all the words "Have", "A", "Good", "Day" are in the array

"itIsAwfullyColdDayToday" // should return false since only the word "day" is in the array

"HelloStackoverflow" // should return true since both words "Hello" and "Stackoverflow" are in the array

As suggested in this other SO thread, I tried to use the every() method and the indexOf() method to test if every split string can be found in the array or not as seen in the following Code Snippet but it's not working:

var arr = ["hello", "have", "a", "good", "day", "stackoverflow"];

function checkString(wordArray, str)
{
    // split the camelCase words
    var x = str.replace(/([A-Z])/g, ' $1').split(" ");

    return x.every(e => {
        return wordArray.indexOf(e.toLowerCase()) >= 0;
    });
}

console.log("should return true ->" + checkString(arr, "HelloStackoverflow"));
console.log("should return false ->" + checkString(arr, "itIsAwfullyColdDayToday"));

What am I doing wrong?

AndrewL64
  • 15,794
  • 8
  • 47
  • 79

5 Answers5

3

You have to convert your split strings to lowercase before doing the comparison.

Also, you have to remove the empty string at the beginning of the list which is inserted by replace when the first letter of your string is uppercase.

You can use includes instead of indexOf.

var arr = ["hello", "have", "a", "good", "day", "stackoverflow"];

function checkString(wordArray, str) {
    return str.replace(/([A-Z])/g, ' $1')
      .split(" ") // split on spaces
      .map(s => s.toLowerCase())
      .filter(s => s)
      .every(e => wordArray.includes(e));
}

console.log("should return true -> " + checkString(arr, "HelloStackoverflow"));
console.log("should return false -> " + checkString(arr, "itIsAwfullyColdDayToday"));
jo_va
  • 13,504
  • 3
  • 23
  • 47
  • I specified the toLowerCase() but copied the wrong snippet earlier. And I overlooked the empty split string on cases where the string starts with an uppercase letter. Thanks man. +1 – AndrewL64 Jan 28 '19 at 19:59
  • 2
    what is the purpose of `.filter(s => s)` ? – Taki Jan 28 '19 at 20:03
  • @Taki `["","hello"].filter(f => f)` will result in `["hello"]`, it's just a simple way of getting rid of elements with blank strings. – Keith Jan 28 '19 at 20:25
  • @Taki I think that filter removes the empty string generated when, for example, `Hello` is replaced by `Hello` and then splitted by `space` generating the array `["", "Hello"]`. In the previous version of his post there is a commentary about this. – Shidersz Jan 28 '19 at 20:26
  • This is a good answer but the `lookahead assertion` approach is way more concise imo. Rather than dealing with the generated empty string, `lookahead assertion` makes sure the empty string is not even generated in the first place. – AndrewL64 Jan 28 '19 at 20:37
3

For this particular case, I will use a lookahead assertion (?=...), which is a non-capturing construct and I will use it directly with the String::split() method. This will solve the problem of the extra generated empty string element on your array when the string begins with an uppercase letter. And also I will give a try to Array::includes() in exchange of indexOf()

const arr = ["hello", "have", "a", "good", "day", "stackoverflow"];

function checkString(wordArray, str)
{
    return str.split(/(?=[A-Z])/g).every(
        e => wordArray.includes(e.toLowerCase())
    );
}

console.log(
    "checkString(arr, 'HelloStackoverflow') =>",
    checkString(arr, "HelloStackoverflow")
);
console.log(
    "checkString(arr, 'itIsAwfullyColdDayToday') =>",
    checkString(arr, "itIsAwfullyColdDayToday")
);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
Shidersz
  • 16,846
  • 2
  • 23
  • 48
  • 1
    Didn't think of using `lookahead assertion` earlier. Definitely a good approach for this. Thanks man. +1 – AndrewL64 Jan 28 '19 at 20:06
1

you were very close, but there was 2 problems:

  • one of them was on your str.replace, it was returning something like ["", "Hello", "Stackoverflow"] when you had a uppercase letter on the start of the string.

  • the second one was on your comparison wordArray.indexOf() it is case sensitive, so you needed to add toLowerCase() so it ends up like this: wordArray.indexOf(e.toLowerCase())

var arr = ["hello", "have", "a", "good", "day", "stackoverflow"];

function checkString(wordArray, str) {
  var x = str.replace(/([A-Z])/g, ' $1').split(" "); // split the camelCase words
  //filter spaces
  var filtered = x.filter(s => s != '');
  return filtered.every(e => {
    return wordArray.indexOf(e.toLowerCase()) >= 0;
  });
}

console.log("should return true ->" + checkString(arr, "HelloStackoverflow"));
console.log("should return false ->" + checkString(arr, "itIsAwfullyColdDayToday"));
Prince Hernandez
  • 3,623
  • 1
  • 10
  • 19
  • I specified the toLowerCase() but copied the wrong snippet earlier. And I overlooked the empty split string on cases where the string starts with an uppercase letter. Thanks man. +1 – AndrewL64 Jan 28 '19 at 19:59
1

A few problems: first, all the arr values start with lowercase letters, but your camelCase tests (of course) contain caps. For that reason I've replaced

wordArray.indexOf(e)

with

wordArray.indexOf(e.toLowerCase())

Second, since your first test case starts with an uppercase letter, your regex is prepending a space, which then gets split into its own "word". To deal with this I've just added !e || to the every condition, so it will always return true for empty strings generated by leading caps.

var arr = ["hello", "have", "a", "good", "day", "stackoverflow"];

function checkString(wordArray, str) {
    var x = str.replace(/([A-Z])/g, ' $1').split(" "); // split the camelCase words
    return x.every(function (e) { return !e || wordArray.indexOf(e.toLowerCase()) >= 0; });
}

console.log("should return true ->" + checkString(arr, "HelloStackoverflow"));
console.log("should return false ->" + checkString(arr, "itIsAwfullyColdDayToday"));
nvioli
  • 4,137
  • 3
  • 22
  • 38
  • I specified the toLowerCase() but copied the wrong snippet earlier but this is a nifty way of dealing with the initial empty string. +1 – AndrewL64 Jan 28 '19 at 20:02
1

This is a pretty simple version.

const checkString = arr => str => str
  .split(/(?=[^a-z])/)
  .every(s => arr.includes(s.toLowerCase()))

const arr = ["hello", "have", "a", "good", "day", "stackoverflow"]

console.log(checkString(arr)('whenTheDayAndNightCollides'))
console.log(checkString(arr)('HaveAGoodDay'))
console.log(checkString(arr)('itIsAwfullyColdDayToday'))
console.log(checkString(arr)('HelloStackoverflow'))

Of course you could also name the intermediate function:

const correct = checkString(arr)
correct('HaveAStackoverflowDay') //=> true
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103