-1

I have an array of strings and I am attempting to filter the array of strings that contain repeating letters. However, two weird things are happening that I don't understand. Here is my code:

var array = ["aba", "aab", "baa"];

var pattern = /(\D)\1+/gi;

var filteredArr = array.filter(function(element){
    console.log(element);
    console.log(pattern.test(element));
    return pattern.test(element) != true;
});

console.log(filteredArr);

Some weird things happen. Within the filter function, I test if the regular expression is true or false and that goes as it should.

pattern.test("aba") = false;
pattern.test("aab") = true;
pattern.test("baa") = true;

However, if I test them outside of the function, "baa" seems to return false...which is weird right?

console.log(pattern.test("aba")); //returns false
console.log(pattern.test("aab")); //returns true
console.log(pattern.test("baa")); //returns false

Onto the next weird thing. The filter function SHOULD return the elements that do NOT pass (ie return false) the filter test. My expected output would be:

filteredArr = ["aba"];

However, with the way the code is, my output is:

filteredArr = ["aba", "aab", "baa"];

What's even more strange is that if I change the filter function to return the elements that DO pass (ie return true) the test, the expected output would be:

filteredArr = ["aab", "baa"];

However, the output that I receive is an empty array:

filteredArr = [];

I'm super confused. Is my regular expression wrong or am I perhaps attempting something that the filter function isn't able to do? Here is a fiddle with all of the code:

My fiddle

ewrjontan
  • 49
  • 1
  • 7

3 Answers3

2

The strange behavior you're seeing is the result of the g modifier. Every call to test is advancing the pattern's lastIndex property, which makes the next call to test() begin at a later point in the string.

Here's the MDN description of the lastIndex property:

This property is set only if the regular expression instance used the "g" flag to indicate a global search. The following rules apply:

  • If lastIndex is greater than the length of the string, test() and exec() fail, then lastIndex is set to 0.
  • If lastIndex is equal to the length of the string and if the regular expression matches the empty string, then the regular expression matches input starting at lastIndex.
  • If lastIndex is equal to the length of the string and if the regular expression does not match the empty string, then the regular expression mismatches input, and lastIndex is reset to 0.
  • Otherwise, lastIndex is set to the next position following the most recent match.

You can verify this by adding console.log(pattern.lastIndex); to your filter:

var array = ["aba", "aab", "baa"];

var pattern = /(\D)\1+/gi;

var filteredArr = array.filter(function(element){
  var test = pattern.test(element);
  console.log(element + ": " + test);
  console.log(pattern.lastIndex);
  return test;
});

console.log(filteredArr);

To fix your code, remove the g flag from the regex.

4castle
  • 32,613
  • 11
  • 69
  • 106
0

You're using a global regexp with .test which is problematic.

Consider the following dev console trace

$ var pattern = /(\D)\1+/gi;
undefined
$ pattern.lastIndex
0
$ pattern.test('xx')
true
$ pattern.lastIndex
2
$ pattern.test('xx')
false
$ pattern.lastIndex
0

The problem that you're running into is that RegExps with the g flag resume checking at lastIndex, so if you check the same string twice in a row, you get different results.

You can manually reset pattern.lastIndex = 0 or use a non-global RegExp.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
0

This is working example.

var array = ["aba", "aab", "baa","bba","bab"];


var filteredArr = array.filter(function(element){
    console.log(element);
    //note that a constant is used instead of the var
    var t = /(\D)\1+?/gi.test(element);
    console.log(t);
    return !t;
});

console.log(filteredArr);

The issue with your code is here:

Use test() whenever you want to know whether a pattern is found in a string (similar to the String.prototype.search() method, difference is that test() returns a boolean, whilst search() returns the index (or -1 if not found); for more information (but slower execution) use the exec() method (similar to the String.prototype.match() method). As with exec() (or in combination with it), test() called multiple times on the same global regular expression instance will advance past the previous match.

RegExp.prototype.test()

Alex Kudryashev
  • 9,120
  • 3
  • 27
  • 36