I have a string list:

lis = {"abc","def","def ghi","jkl","ghi","jkl"}

I have another string list whose elements contain two members of lis joined together and separated by a space character:

lis2 = {"abc jkl","def ghi ghi","jkl abc"}

I would like to separate each member of lis2 back into its two components that belong to lis:

res = {{"abc","jkl"},{"def ghi","ghi"},{"jkl","abc"}}

As always, thanks for any ideas.

Here is another sample data set that may state the problem more clearly.

newLis = {"Texas State","Ohio","Sam Houston State","Ohio State"}

newLis2 = {{"Texas","State","Ohio","State"},{"Sam","Houston","State","Ohio","State"},{"Ohio","Sam","Houston","State"}}

Each of the three elements of newLis2 needs to be decomposed into two elements which are members of newLis:

newRes = {{"Texas State","Ohio State"},{"Sam Houston State","Ohio State"},{"Ohio","Sam Houston State"}}

*Please see kglr's comment below, he clearly states the problem at (1). It is best not to make the responders have to guess at the questioner's question :/ *

  • 1
    $\begingroup$ StringSplit /@ lis2 is your after? $\endgroup$ – yode Sep 13 '18 at 2:38
  • $\begingroup$ No, that gives: {{"abc", "jkl"}, {"def", "ghi", "ghi"}, {"jkl", "abc"}} and I'm aiming for {{"abc","jkl"},{"def ghi","ghi"},{"jkl","abc"}} $\endgroup$ – Suite401 Sep 13 '18 at 2:57
  • $\begingroup$ Suite401, updated problem and the original one are very different questions. Maybe you should make it explicit that the question is a two-part one: (1) Split the strings in input list so that the resulting strings match the ones in a reference list (lis) , and (2) reorganize and StringJoin a list of strings so that the resulting strings match the ones in the references list (lis). $\endgroup$ – kglr Sep 13 '18 at 5:30
  • $\begingroup$ Yes I agree, kglr - thank you for clarifying. This is exactly what I am trying to do. $\endgroup$ – Suite401 Sep 13 '18 at 5:46
  • $\begingroup$ @kglr yes, the task with the second data sample is what you describe at (1) in your comment. Thank you all for your patience and advice. $\endgroup$ – Suite401 Sep 13 '18 at 5:53
alternatives = Apply[Alternatives] @ Reverse @ SortBy[StringLength] @ newLis

StringCases[alternatives] /@  StringRiffle /@ newLis2
"Sam Houston State" | "Texas State" | "Ohio State" | "Ohio" 

{{"Texas State", "Ohio State"}, 
 {"Sam Houston State", "Ohio State"}, 
 {"Ohio", "Sam Houston State"}

Reverse sort is done to make the longes cases more imprtant. E.g. to match Ohio State rather than single Ohio.

  • $\begingroup$ Thank you all for your responses. @Kuba your solution fails on my data set, but if I replace StringCases[alternatives] /@ StringRiffle /@ newLis2 with StringCases[alternatives] /@ StringRiffle /@ newLis2[1] it succeeds. I'll call that a win :) $\endgroup$ – Suite401 Sep 13 '18 at 21:31


StringReplace[lis2,   a__ ~~ " " ~~  b__ /;
   (And @@ (Or @@ StringMatchQ[lis, #]& /@ {a, b})) :> {a,b}] /.
  StringExpression -> Identity

{{"abc", "jkl"}, {"def ghi", "ghi"}, {"jkl", "abc"}}

StringReplace[lis2,  StartOfString ~~ a : Alternatives @@ lis ~~ 
    " " ~~ b : Alternatives @@ lis ~~ EndOfString :> {a, b}] /. 
 StringExpression -> Identity  

{{"abc", "jkl"}, {"def ghi", "ghi"}, {"jkl", "abc"}}


DeleteCases[SequenceAlignment[#, StringJoin @ 
  Riffle[PadRight[lis, 2 Length@lis, "Periodic"], "/"]], _List |  " "] & /@ lis2

{{"abc", "jkl"}, {"def ghi", "ghi"}, {"jkl", "abc"}}

  • $\begingroup$ When I use kglr's second suggestion on the new sample data set, I get the following error message: StringReplace::strse: String or list of strings expected at position 1 in StringReplace[{{Texas,State,Ohio,State},{Sam,Houston,State,Ohio,State},{Ohio,Sam,Houston,State}},StartOfString~~a:Texas State|Ohio|Sam Houston State|Ohio State~~ ~~b:Texas State|Ohio|Sam Houston State|Ohio State~~EndOfString:>{a,b}]. $\endgroup$ – Suite401 Sep 13 '18 at 5:27
  • $\begingroup$ @Suite401, just saw your update. This one is not intended to work for your updated problem. Please see my comment below your question. $\endgroup$ – kglr Sep 13 '18 at 5:31

Use StringSplit at first and then deal with the special case

If[Length[#] == 3, {StringRiffle[#[[;; 2]]], #[[3]]}, #] & /@ StringSplit[lis2]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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