# Partition a list of rules into sublists based on a value string pattern

lis={"#49:5"->"#61:7", "#49:5"->"#62:5", "#49:6"->"#61:8", "#49:6"->"#62:4", "#49:7"->"#61:10", "#49:7"->"#62:3", "#49:8"->"#61:9", "#49:8"->"#62:6"}


I would like to partition this list of rules into a number of sublists that is based on value string patterns #61:x, #62:y,.... #6n:z

clusters=Flatten@DeleteDuplicates@StringCases[Values@mrules,"#"~~cluster__~~":"->cluster]

Out= {61,62}


Therefore this list of rules can be split into two sublists (ordering is not required here):

 {{"#49:5"->"#61:7", "#49:6"->"#61:8", "#49:7"->"#61:10", "#49:8"->"#61:9"}, {"#49:5"->"#62:5", "#49:6"->"#62:4", "#49:7"->"#62:3", "#49:8"->"#62:6"}}


I can also take the list of values sorted

Sort@Values@lis

Out = {"#61:10", "#61:7", "#61:8", "#61:9", "#62:3", "#62:4", "#62:5", "#62:6"}


Then I experimented with Cases, Split, Select, StringMatchQ, etc without any luck.

Could you please assist me with this problem ?

One way:

label[_ -> s_] := StringReplace[s, "#" ~~ n__ ~~ ":" ~~ __ :> n]
GatherBy[lis, label]

(* Out: {{"#49:5" -> "#61:7", "#49:6" -> "#61:8", "#49:7" -> "#61:10",
"#49:8" -> "#61:9"}, {"#49:5" -> "#62:5", "#49:6" -> "#62:4",
"#49:7" -> "#62:3", "#49:8" -> "#62:6"}} *)

• A very clever and elegant solution indeed, thank you C.E. May 31, 2016 at 1:44
• For those who want to avoid ToExpression[]: GatherBy[lis, StringCases[Last[#], "#" ~~ d : DigitCharacter .. ~~ __ :> d] &] May 31, 2016 at 10:44
• @J.M. You posting this made me realize that ToExpression was superfluous in the first place, because they're just labels. They're not used for anything other than checking for equality. So I removed it in the answer. May 31, 2016 at 12:05

Just for another approach (based on grouping by "#6n". Using GroupBy:

lis = {"#49:5" -> "#61:7", "#49:5" -> "#62:5", "#49:6" -> "#61:8",
"#49:6" -> "#62:4", "#49:7" -> "#61:10", "#49:7" -> "#62:3",
"#49:8" -> "#61:9", "#49:8" -> "#62:6"};
gp = GroupBy[lis, StringTake[#[[2]], {2, 3}] &, Rule @@@ # &];
v = Values[gp]


To sort by rule value:

f = ToExpression[StringSplit[StringDrop[#, 1], ":"]] &;
SortBy[#, f@#[[2]] &] & /@ v


yields:

{{"#49:5" -> "#61:7", "#49:6" -> "#61:8", "#49:8" -> "#61:9",
"#49:7" -> "#61:10"}, {"#49:7" -> "#62:3", "#49:6" -> "#62:4",
"#49:5" -> "#62:5", "#49:8" -> "#62:6"}}

• Thank you, always eager to learn new things ;-) May 31, 2016 at 6:00
• @Athanassios Παρακαλώ. I voted for C.E. I just wanted to show another way. Mma is versatile. I learn a lot from the creativity of the users on this site myself. :) May 31, 2016 at 6:04
• Hi, I studied a bit your code. I noticed that the third argument of GroupBy, i.e. (Rule @@@ # &) can be omitted ??? And I am trying hard to understand this SortBy. Would you be kind enough to provide some analysis, e.g. rewrite it in a more comprehensible way. This f@#[[2]]& is really too difficult to grasp. Thank you Jun 4, 2016 at 8:51
• I can see that : f /@ Values /@ Values@gp {{{61, 7}, {61, 8}, {61, 10}, {61, 9}}, {{62, 5}, {62, 4}, {62, 3}, {62, 6}}} But I cannot understand how this f@#[[2]]& is applied on Values@gp Jun 4, 2016 at 8:57
• @Athanassios f@#[[2]& is just using the function fon the second part of the argument. In this case the second argument is the value of the rule. The value is a string (a pair of numbers separate by ':'). f converts this into an expression (numerical) that SortBy can use to order. I hope this is clearer. # is just a placeholder and & is used for completion pure functions.:) Jun 4, 2016 at 8:57
Clear["Global*"]
lis = {"#49:5" -> "#61:7", "#49:5" -> "#62:5", "#49:6" -> "#61:8",
"#49:6" -> "#62:4", "#49:7" -> "#61:10", "#49:7" -> "#62:3",
"#49:8" -> "#61:9", "#49:8" -> "#62:6"}

GatherBy[lis, StringTake[Values@#, {2, 3}] &]


or

GatherBy[lis, First@StringCases[#, NumberString] &@*Values]
`

Result

{{"#49:5" -> "#61:7", "#49:6" -> "#61:8", "#49:7" -> "#61:10",
"#49:8" -> "#61:9"}, {"#49:5" -> "#62:5", "#49:6" -> "#62:4",
"#49:7" -> "#62:3", "#49:8" -> "#62:6"}}