8
$\begingroup$

I have a sample list that includes people's names in Title Case, and their companies in UPPER CASE.

list = {"Bob Jones", "ACME-SYSTEMS", "John Smith", "FUTURETECH123", 
  "Sally Jones", "CITY SCHOOL", "Jane Black", "CONSULTANT", 
  "Max Speed", "A.B. CORP"}

I would like to select only the list items that are in upper case. The most obvious approach would be to use UpperCaseQ:

Select[list, UpperCaseQ]
(*{"CONSULTANT"}*)

Unfortunately, UpperCaseQ returns FALSE for any list item that has a non letter characters such as space or punctuation in it.

I did find that I can incorporate StringReplace within UpperCaseQ to ignore some symbols:

Select[list, UpperCaseQ[StringReplace[#, {" ", "-", "."} -> ""]] &]
(*{"ACME-SYSTEMS", "CITY SCHOOL", "CONSULTANT", "A.B. CORP"}*)

Besides spaces, periods and hyphens, there is also the issue of numbers and symbols in strings (hence FUTURETECH123 being off the list). On a small dataset, you can hand code the exceptions, but as you get larger, you would need to automate it.

I was able to come up with this code as a way to automatically select the non letter characters, but I would think running the code on a very large document would not be very efficient.

allcharacters = 
 DeleteDuplicates[Flatten[Characters[list]]]; nonletters = 
 Select[allcharacters, LetterQ[#] == False &];
Select[list, UpperCaseQ[StringReplace[#, nonletters -> ""]] &]
(*{"ACME-SYSTEMS","FUTURETECH123","CITY SCHOOL","CONSULTANT","A.B. CORP"}*)

Is there a more simple way to select items in a list that are UPPER CASE while ignoring the non letter characters?

$\endgroup$

4 Answers 4

12
$\begingroup$

The following code might not be quite fast but it takes into account foreign languages.

list = {"Bob Jones", "ACME-SYSTEMS", "John Smith", "FUTURETECH123", 
   "Sally Jones", "CITY SCHOOL", "Jane Black", "CONSULTANT", 
   "Max Speed", "A.B. CORP", "TЯUMP", "яUSSIam"};
Select[list, ToUpperCase[#] === # &]

You can see it catches the Russian TЯUMP.


UPDATE

Apparently, Russians cannot be caught. In the example below, an accented letter á (as those used in Spanish) is processed by the system but the Russian one is quite stealthy.

string = "áа";
ToCharacterCode[string]
ToUpperCase[string]
(*
{225, 1072}
"Áа"
*)
$\endgroup$
3
  • 3
    $\begingroup$ What a simple solution. Now just to make sure I am following correctly, the code is essentially selecting all items in the list where using ToUpperCase produces the exact same result as the original item. Right? $\endgroup$
    – kickert
    Commented Jul 19, 2018 at 20:38
  • 2
    $\begingroup$ @kickert Yes, exactly. $\endgroup$
    – Hector
    Commented Jul 19, 2018 at 20:42
  • 7
    $\begingroup$ "Russian TЯUMP." That's a good one! ;) $\endgroup$ Commented Jul 19, 2018 at 20:47
5
$\begingroup$

The simplest regex that I can think of that does what the OP asks is the following:

list // Pick[#, StringContainsQ[#, RegularExpression["^[^a-z]+$"]]] &

"ACME-SYSTEMS", "FUTURETECH123", "CITY SCHOOL", "CONSULTANT", "A.B. \ CORP"}

For the cases discussed by Hector, maybe:

Join[list, {"TЯUMP", "яUSSIam", "ЯЯ", "TЯЯUMP", "Яя", "TЯяUMP"}] 
// Pick[#, StringContainsQ[#, RegularExpression["^[^a-zа-я]+$"]]] &

{"ACME-SYSTEMS", "FUTURETECH123", "CITY SCHOOL", "CONSULTANT", "A.B. \ CORP", "TЯUMP", "ЯЯ", "TЯЯUMP"}

Original WRONG answer

list2 // Pick[#, StringContainsQ[#, RegularExpression["[ALPHA]"]]] &

{"ACME-SYSTEMS", "FUTURETECH123", "CITY SCHOOL", "CONSULTANT", "A.B. \ CORP", "TЯUMP"}

A very beautiful solution, which gives the right answer. Things began to unravel, however, when I read Hector's comment, got an unexpected result, and tried the following:

 list// Pick[#, StringContainsQ[#, RegularExpression["[ALPHB]"]]]&

{"Bob Jones", "ACME-SYSTEMS", "FUTURETECH123", "CITY SCHOOL", "Jane \ Black", "CONSULTANT", "A.B. CORP"}

$\endgroup$
3
  • 1
    $\begingroup$ A coworker of mine just suggested looking into RegularExpression for this problem. Seems like a great option for more complicated string manipulations. Thanks for the alternative approach. $\endgroup$
    – kickert
    Commented Jul 19, 2018 at 21:35
  • $\begingroup$ @tomd My version of MMA does not have StringContainsQ. Do you mind checking the result of your approach on "Яя". I noticed MMAv9 does not classify the Russian alphabet (even thought my OS does). I am wondering whether regular expressions in MMA are any better in latter versions of MMA. $\endgroup$
    – Hector
    Commented Jul 19, 2018 at 21:59
  • $\begingroup$ @Hector. On reading your comment, I realized my original solution was wrong! I have modified it to use the simplest regex that I can think of that does what the OP asks. I prefer your solution. $\endgroup$
    – user1066
    Commented Jul 20, 2018 at 10:40
3
$\begingroup$
list = {"Bob Jones", "ACME-SYSTEMS", "John Smith", "FUTURETECH123", 
  "Sally Jones", "CITY SCHOOL", "Jane Black", "CONSULTANT", 
  "Max Speed", "A.B. CORP"}

Pick[list, StringContainsQ[Alternatives @@ Alphabet[]] /@ list
 , False
 ]

OR

Pick[list, StringFreeQ[Alternatives @@ Alphabet[]] /@ list
 , True
 ]

OR

Select[list, 
 UpperCaseQ@
   StringDelete[
     PunctuationCharacter | DigitCharacter | Whitespace][#] &
 ]

{"ACME-SYSTEMS", "FUTURETECH123", "CITY SCHOOL", "CONSULTANT", "A.B. CORP"}

$\endgroup$
2
$\begingroup$
list = 
 {"Bob Jones", "ACME-SYSTEMS", "John Smith", "FUTURETECH123", 
  "Sally Jones", "CITY SCHOOL", "Jane Black", "CONSULTANT", 
  "Max Speed", "A.B. CORP"}

A variant of Syed's solution using ContainsNone

Pick[list, ContainsNone @ Alphabet[] /@ Characters @ list]

{"ACME-SYSTEMS", "FUTURETECH123", "CITY SCHOOL", "CONSULTANT",
"A.B. CORP"}

$\endgroup$

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.