Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

If I have a list of n number of rules, what is a concise way of breaking the data into 2 lists of the first and second part of the rule?

This works, but seems like an unnecessary amount of code to me:

data = {
  1 -> 3, 1 -> 3, 1 -> 3, 2 -> 3, 2 -> 3, 2 -> 1, 
  3 -> 2, 3 -> 2, 3 -> 3, 4 -> 1, 4 -> 2, 4 -> 2
};

list1 = First[Partition[Flatten[data /. Rule -> List], 2, 2, 1, {}]~Flatten~{2}]
{1,1,1,2,2,2,3,3,3,4,4,4}
list2 = Last[Partition[Flatten[data /. Rule -> List], 2, 2, 1, {}]~Flatten~{2}]
{3,3,3,3,3,1,2,2,3,1,2,2}
share|improve this question
    
{First[#], Last[#]} & /@ data // Transpose –  alancalvitti Aug 6 at 14:18
    
or {list1, list2} = {data[[All, 1]], data[[All, 2]]} –  Mike Honeychurch Aug 7 at 0:06

6 Answers 6

up vote 8 down vote accepted

This is the shortest I can think of:

{list1, list2} = Transpose[List @@@ data];

list1
{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4}
list2
{3, 3, 3, 3, 3, 1, 2, 2, 3, 1, 2, 2}}
share|improve this answer
data[[All, #]] & /@ {1, 2}
{{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4}, {3, 3, 3, 3, 3, 1, 2, 2, 3, 1, 2, 2}}
share|improve this answer
1  
or data[[;; , #]] & /@ {1, 2} to shave off one character. –  seismatica Aug 6 at 19:13
data // Query[{Keys, Values}]

or Through@{Keys, Values}@data, or list1=Keys@data; list2=Values@data

share|improve this answer
    
Darn, why didn't I think of that. +1 –  Mr.Wizard Aug 6 at 20:49
2  
As far as I'm concerned, Through[{Keys,Values}[data]] is the proper answer to this question. +1. Unfortunately, Through is a bit of a weird function. We're discussing a new function called maybe ComposeThrough, giving ComposeThrough[{Keys,Values}, data] in this example (and of course would have an operator form). This would formalize the {op,..} and <|key->op,...|> sugar in Query (and make it faster). –  Taliesin Beynon Aug 7 at 18:32
    
@TaliesinBeynon Sounds great but please also give it a short form (like /@ @* etc.) so that I'll actually use it. By the way I'd still like to chat. Do you have time today? –  Mr.Wizard Aug 7 at 20:02

user21 and Teake Nutma already posted the two methods I use most often. Of these I recommend Part as I believe it will be faster in general. Nevertheless these are hardly the only ways to accomplish this task. First, since the expression produced by List @@@ data will not be packed Thread may be faster than Transpose:

Thread[List @@@ data]

One could also thread over Rule, then replace the outer head with List:

List @@ Thread[data, Rule]

From Undocumented form for Extract we could also use:

Rest @ Extract[data, {{0}, {All, 1}, {All, 2}}]

Benchmarks

A BenchmarkPlot for all methods posted so far.

f1[a_] := a[[All, #]] & /@ {1, 2};
f2 = Transpose[# /. (a_ -> b_) :> {a, b}] &;
f3 = Transpose[List @@@ #] &;
f4 = Thread[List @@@ #] &;
f5 = List @@ Thread[#, Rule] &;
f6 = Rest@Extract[#, {{0}, {All, 1}, {All, 2}}] &;
f7 = Query[{Keys, Values}];
f8 = {Keys@#, Values@#} &

Needs["GeneralUtilities`"]
g[n_] := Rule @@@ RandomInteger[9, {n, 2}];

BenchmarkPlot[{f1, f2, f3, f4, f5, f6, f7, f8}, g, 2^Range[5, 20], "IncludeFits" -> True]

enter image description here (click for larger)

  • Once again Query has some crazy overhead and should be avoided when performance matters unless inputs are very large.

  • Keys and Values are very fast when used apart from Query.

  • List @@ Thread[data, Rule] is as fast as Keys and Values, and faster than Part and Extract which I did not expect.

  • As expected Thread is slightly faster than Transpose with unpacked data. (f3 and f4)

share|improve this answer
    
Could you add {First /@ #, Last /@ #} &? –  Teake Nutma Aug 7 at 21:08
    
@TeakeNutma That is quite a bit slower than the present leaders and I'd rather not clutter the graph any further. It is the upper line on this plot: i.stack.imgur.com/0gKsU.png compared to f5 and f8. (Ignore the legend.) –  Mr.Wizard Aug 7 at 21:18
{#1, #2} & @@@ data // Transpose
{{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4}, {3, 3, 3, 3, 3, 1, 2, 2, 3, 1, 2, 2}}
share|improve this answer

You can also use HoldPattern:

data /.HoldPattern[a_ -> b_] :> {a, b} // Transpose
{{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4}, {3, 3, 3, 3, 3, 1, 2, 2, 3, 1, 
  2, 2}}
share|improve this answer
    
You can indeed, but it's not necessary to hold the LHS of the rule. data /. (a_ -> b_) -> {a, b} // Transpose or even data /. Rule -> List // Transpose work just as fine. –  Teake Nutma Aug 6 at 15:28
    
yep, you're absolutely right. –  gpap Aug 6 at 15:36
    
@Teake and gpap, you both forgot to localize your pattern names; use :> instead! –  Mr.Wizard Aug 6 at 20:51
    
@Mr.Wizard I claim a typo! –  Teake Nutma Aug 6 at 20:54
    
done! thanks for the comment - this was written as hastily as they come. –  gpap Aug 7 at 8:44

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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