3

I got a string, and an array of strings. Examples of the string are:

let example1 = "_a_t_";
let example2 = "N_l_:t___";
let example3 = "B__z_l";

What we can see is that the string is Z characters long and got a random amount of characters that is not _. The string will never be only _ nor will it ever be without an underscore.

let array = ["Walts", "Grate", "Ralts", "Names"];

If we use the example1 string and the array above, I want to filter the array so the outcome becomes:

let newArray = ["Walts", "Ralts"];

Basically, of the known characters I want to use them and their position to filter the array. Currently I've found how to find the first character and use it to filter the array. This is how that was done:

let example1 = "_a_t_";
let array = ["Walts", "Grate", "Ralts", "Names"];

let charIndex = example1.match("[a-zA-Z]").index;
/*Currently using the RegEx [a-zA-Z], but this should be changed to include all characters 
besides underscores but I don't know what to put in between the square brackets*/
let firstChar = example1.charAt(charIndex);

let filteredArray = array.filter(function (element) {
  return element.charAt(charIndex) === firstChar;
});

console.log(filteredArray); //this returns ["Walts", "Ralts", "Names"]

It is here I am stuck. I got no idea how to do this if I got multiple revealed characters in the string. After pondering a bit, the logical thing to me would be to somehow count all the characters that's not an underscore, then using that, make a loop. The loop would find each character and its index, then filter the array. The loop would finish when the array is completely filtered. Using the above array and example1 string, the wished goal would be to get ["Walts", "Ralts"].

I think that the problem is thoroughly explained, and that the end goal is clear. This is my first ever post on Stack Overflow so I'm very excited.

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
OscaHar
  • 33
  • 4

2 Answers2

2

I think this might be easier if you create a regular expression from each pattern and just test each string against the whole pattern. Here's a quick proof of concept implementation.

It just replaces _ with . when creating the expression so underscore will match any character, then filters the array according to whether the string is a match.

For example, the string "_a_t_" becomes the regex /^.a.t.$/gi which matches Walts and Ralts but not Grate or Names.

The regex pattern /^.a.t.$/gi loosely translates to:

  • ^ start of string
  • . followed by any character
  • a followed by literal 'a'
  • . followed by any character
  • t followed by literal 't'
  • . followed by any character
  • $ end of string.

const example1 = "_a_t_";
const example2 = "N_l_:t___";
const example3 = "B__z_l";
const array = ["Walts", "Grate", "Ralts", "Names"];

// turn the string patterns into regular expressions
const regexes = [example1, example2, example3].map(pattern => {
  return new RegExp(`^${pattern.replaceAll('_', '.')}$`, 'i');
});

// filter out names that don't match any of the expressions
const result = array.filter(name => regexes.some(r => r.test(name)));
  
console.log(result);

Edit: Updated to streamline the sample implementation.

ray
  • 26,557
  • 5
  • 28
  • 27
  • 1
    [The forth bird's solution](https://stackoverflow.com/a/66912432/636077) is the same core idea but by joining the expressions into a single pattern his is more efficient. You could tweak mine to do the same by replacing my `map` iteration with a `join('|')` and creating a single RegEx. – ray Apr 01 '21 at 22:47
  • Ack. "fourth". Oops. – ray Apr 01 '21 at 23:10
2

Another option could be to create a single regex and join all the words with in a non capture group with the | as a separator and do a single replacement for the _ to \S to match a non whitespace char to not match spaces

The pattern will look like:

^(?:\Sa\St\S|N\Sl\S:t\S\S\S|B\S\Sz\Sl)$

The pattern matches:

  • ^ Start of string
  • (?: Non capture group
    • \Sa\St\S First alternative, where \S matches a non whitespace char
    • | Or
    • N\Sl\S:t\S\S\S Second alternative
    • | Or
    • B\S\Sz\Sl Third alternative
  • ) Close non capture group
  • $ End of string

For example

let example1 = "_a_t_";
let example2 = "N_l_:t___";
let example3 = "B__z_l";
let array = ["Walts", "Grate", "Ralts", "Names"];
const strings = [example1, example2, example3];
const regex = new RegExp(`^(?:${strings.join("|").replace(/_/g, "\\S")})$`);
console.log(array.filter(s => regex.test(s)));

If you want to match a space, you can use a . instead of \S

Note that in the RegExp() constructor, you have to double escape the backslash. If any of the strings contain a character special to the regex, you have to escape those characters.

Another example:

const escapeRegExp = s => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
let example1 = "_a_t_";
let example2 = "N_l_:t___";
let example3 = "B__z_l";
let example4 = "T__()"
let array = ["Walts", "Grate", "Ralts", "Names", "Tab()"];
const strings = [example1, example2, example3, example4].map(s => escapeRegExp(s));
const regex = new RegExp(`^(?:${strings.join("|").replace(/_/g, ".")})$`);
console.log(array.filter(s => regex.test(s)));
The fourth bird
  • 154,723
  • 16
  • 55
  • 70
  • Thanks for the answer! It works in the code snippet and when put into a js file connected to a HTML file. I however got a problem with your solution (and with ray hatfield's solution). I ran this snippet in a node project and got the error `TypeError: strings.join(...).replaceAll is not a function`. How do I solve this? – OscaHar Apr 01 '21 at 23:03
  • 1
    @OscaHar I have updated it to use replace. – The fourth bird Apr 01 '21 at 23:04
  • 1
    [replaceAll](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll) is relatively new and [isn't supported](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll#browser_compatibility) in all environments. Node added support in 15.0. You might need to use a regex for the replace call instead of replaceAll if you want it to work everywhere. – ray Apr 01 '21 at 23:05
  • 1
    Many thanks to both of you. It works the way I wanted it, while still being efficient and not being a lot of lines. Appreciate it a bunch! – OscaHar Apr 01 '21 at 23:17