# How to identify and select the first name from a string containing a first name and a last name?

Is it possible to identify and select the first name in a string that is made up of a first name and a last name, e.g.:

names = {"John Smith", "Brown Dave", "Brian Miller"}


There are no guarantees about the order of the first name and the last name in the input string.

The result should be:

res = {"John Smith" -> "John", "Brown Dave" -> "Dave", "Brian Miller" -> "Brian"}


Does Mathematica provide any built-in function(s) for doing this, or has anyone devised a solution to tackle this problem?

Other commonly used terms:

First name ~ Christian name ~ Given name ~ Forename

Last name ~ Family name ~ Surname

We can use the GivenName interpreter type

First we create a dictionary called $rankLookup. names = {"John Smith", "Brown Dave", "Brian Miller"}; uniqueNames = DeleteDuplicates@Flatten@StringSplit@names; rankUniqueNames = EntityValue[Interpreter["GivenName"][uniqueNames], "Rank"];$rankLookup = AssociationThread[uniqueNames, rankUniqueNames]


<|"John" -> 81, "Smith" -> 40149, "Brown" -> 32208, "Dave" -> 6540,
"Brian" -> 864, "Miller" -> 4005|>

We assume that the first name is the most common one, ie has the smallest rank. So we use Min in the function below. This assumption deals with ambiguous names like James Joyce.

guessFirstNameBasedOnRank[name_String] :=
Module[
{
firstString = StringExtract[name, 1],
secondString = StringExtract[name, 2],
firstStringRank,
secondStringRank,
firstNameDicitonary = \$rankLookup,
firstName
},
firstStringRank = firstNameDicitonary[firstString];
secondStringRank = firstNameDicitonary[secondString];
firstName =
First@Pick[{firstString, secondString}, {firstStringRank,
secondStringRank}, Min[firstStringRank, secondStringRank]];
name -> firstName
];


Checking Brown Dave

guessFirstNameBasedOnRank["Brown Dave"]


"Brown Dave" -> "Dave"

guessFirstNameBasedOnRank /@ names


{"John Smith" -> "John", "Brown Dave" -> "Dave", "Brian Miller" -> "Brian"}

Another way to do this using TextCases:

Thread[
names -> Flatten[
TextCases[
Map[StringJoin @* (Riffle[#, " "] &),
Apply[Join, Map[Permutations @* (TextWords[#] &), names]]
],
"GivenName"
]
]
]

(*{"John Smith" -> "John", "Brown Dave" -> "Dave", "Brian Miller" -> "Brian"}*)


Edit: As @IntroductionToProbability suggested:

Thread[
names -> Flatten[
TextCases[
Map[StringRiffle,
Apply[Join, Map[Permutations @* (TextWords[#] &), names]]
],
"GivenName"
]
]
](*Thanks, @IntroductionToProbability!*)

(*{"John Smith" -> "John", "Brown Dave" -> "Dave", "Brian Miller" -> "Brian"}*)

• I looked at TextCases but couldn't get it to work naively on "Brown Dave". It looks like Permutations was the trick I had missed! There is also StringRiffle instead of StringJoin@*(Riffle[#, " "] &) Commented Apr 24, 2023 at 7:29
names = {"John Smith", "Brown Dave", "Brian Miller"};

given =
First@ * Flatten /@ (TextCases[# <> " X" & /@
StringSplit[#], "GivenName"] & /@ names)


{"John", "Dave", "Brian"}

Thread[names -> given]


{"John Smith" -> "John", "Brown Dave" -> "Dave", "Brian Miller" -> "Brian"}