Joshua Ohana
Joshua Ohana

Reputation: 6131

Javascript string match on first or last name, including partials

Say I have a list of users with firstName and lastName and want to filter those who match either first or last names, or both, or any parts, regardless of whitespace. How would I construct that filter?

The closest I've been able to get is

const userString = (user.firstName + user.lastName + user.email).toLowerCase().replace(/\s/g,'');
return userString.includes(filterValue); // where filter value is the thing to test

So if we're working with users = [John Smith, Adam Applebaum, Steve Wright], and I type "jo" or "sm" I correctly get "John Smith", but if I type "josm" I would also want "John Smith", but I can't seem to get that to return.

How can I construct a filter or test to include both first and last names, or any combo therein.

@Nina's answer below worked perfect, but I had to modify a bit for my particular use case, my final implementation was;

  private _filter(value: string): User[] {
    if (!value || typeof value !== 'string') { return this.users; }
    const regexp = new RegExp([...(value.split(" "))].join('.*'), 'i');
    return this.users.filter(user => {
      const userString = user.firstName + ' ' + user.lastName;
      return regexp.test(userString);
    });
  }

Upvotes: 2

Views: 1806

Answers (2)

baartko
baartko

Reputation: 117

@Nina Scholz example is great, but actually I would go a bit different way, as regexp s.*m.*i.*t.*h may give a bit inaccurate results.

e.g.

Searched: sh Results: John Smith
Searched: st Results: John Smith
Searched: ji Results: John Smith

Actually any letter from first name and any from last name, and in this case results are inaccurate.

I would rather go with regexp .*SEARCH_PHRASE.* or to be more exact .*smi.* But this is personal opinion :) just wanted to show the differences.

Final form would be some kind of this:

function buildPattern(str) {
    const arr = [...str];

    // as first item
    arr.splice(0, 0, '.*');

    // as last item
    arr.splice(arr.length, 0, '.*');

    return new RegExp(arr.join(''), 'i');
}

function filter(array, string) {
    return array.filter(RegExp.prototype.test, buildPattern(string));
}

const array = ['John Smith', 'Adam Applebaum', 'Steve Wright'];

console.log(filter(array, "jo"));
console.log(filter(array, "sm"));
console.log(filter(array, "josm"));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386650

You could space each character with a joker and test the string.

function filter(array, string) {
    return array.filter(RegExp.prototype.test, new RegExp([...string].join('.*'), 'i'));
}

var array = ['John Smith', 'Adam Applebaum', 'Steve Wright'];

console.log(filter(array, "jo"));
console.log(filter(array, "sm"));
console.log(filter(array, "josm"));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

Related Questions