6
$\begingroup$

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

$\endgroup$
1

2 Answers 2

4
$\begingroup$

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"}

$\endgroup$
3
$\begingroup$

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"}*)
$\endgroup$
1
  • 1
    $\begingroup$ 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[#, " "] &) $\endgroup$ Commented Apr 24, 2023 at 7:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.