6
$\begingroup$

I have a list of strings, some of which are all upper case, and some mixed upper and lower, and some are digits with commas:

lis = {"ABC","Abc","Def","1","DEF","Ghi","Jkl","MNO","1,"}

I would like to StringJoin adjacent elements that consist of mixed upper and lower cases to give:

res = {"ABC", "AbcDef","1","DEF","GhiJkl","MNO","1,"}

I can identify the elements of lis that contain lower case letters easily enough:

StringContainsQ[tes,CharacterRange["a","z"]

but I don't know how to make a rule to StringJoin adjacent elements that return True. Thanks for suggestions.

$\endgroup$

4 Answers 4

7
$\begingroup$

You can useSequenceReplace:

SequenceReplace[lis, {a__} /; 
 And @@ (StringContainsQ[{a}, Alternatives @@ CharacterRange["a", "z"]]) :> 
   StringJoin[a]] 

% == res

True

Faster alternatives:

SequenceReplace[lis, {a__}/; Nor @@ StringFreeQ[_?LowerCaseQ] @ {a}:> StringJoin[a]]

and

StringJoin /@ Split[lis, Nor @@ StringFreeQ[_?LowerCaseQ] @ {##}&]
$\endgroup$
3
  • $\begingroup$ Seems you beat me by a minute ;) $\endgroup$
    – Lukas Lang
    Commented Sep 16, 2019 at 17:12
  • $\begingroup$ Thank you both for the solutions! They work. Unfortunately my data set is large and both these approaches take some time to execute; any thoughts for further efficiency? $\endgroup$
    – Suite401
    Commented Sep 16, 2019 at 17:48
  • $\begingroup$ @kglr - MUCH better - Thanks! $\endgroup$
    – Suite401
    Commented Sep 16, 2019 at 18:23
5
$\begingroup$

[Edit: Just noticed that @kglr was slightly faster in posting a very similar solution - I'll leave this here since it is at least slightly different, in that it merges arbitrarily many consecutive strings, while @kglr's solution only merges pairs]

You can use SequenceReplace:

SequenceReplace[
  lis,
  {strs__?(StringContainsQ@CharacterRange["a", "z"])} :> 
   StringJoin@strs
]
(* {"ABC", "AbcDef", "1", "DEF", "GhiJkl", "MNO", "1,"} *)
$\endgroup$
2
  • $\begingroup$ Lukas, good point re pairs vs arbitrary consecutive pairs. (+1) $\endgroup$
    – kglr
    Commented Sep 16, 2019 at 17:16
  • 1
    $\begingroup$ i updated with a version that handles sequences of consecutive strings. $\endgroup$
    – kglr
    Commented Sep 16, 2019 at 17:24
5
$\begingroup$

Less elegant but fast solution:

lis //
    {#, StringContainsQ[#, CharacterRange["a", "z"]]} & //      
    Transpose //
    SplitBy[#, Last] & //
    Map[ If[ Last@First@#, StringJoin@(#[[All, 1]]), Sequence @@ (#[[All, 1]]) ] &]
$\endgroup$
2
$\begingroup$
list = {"ABC", "Abc", "Def", "1", "DEF", "Ghi", "Jkl", "MNO", "1,"};

Using ReplaceRepeated

With[{x = Characters[list], p = {_?UpperCaseQ, __?LowerCaseQ}},
 StringJoin /@ ReplaceRepeated[x, {a___, b : p, c : p, d___} :> {a, Join[b, c], d}]]

{"ABC", "AbcDef", "1", "DEF", "GhiJkl", "MNO", "1,"}

$\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.