6
$\begingroup$

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}
$\endgroup$
2
  • $\begingroup$ {First[#], Last[#]} & /@ data // Transpose $\endgroup$ Aug 6, 2014 at 14:18
  • $\begingroup$ or {list1, list2} = {data[[All, 1]], data[[All, 2]]} $\endgroup$ Aug 7, 2014 at 0:06

6 Answers 6

10
$\begingroup$

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}
$\endgroup$
9
$\begingroup$
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}}
$\endgroup$
1
  • 1
    $\begingroup$ or data[[;; , #]] & /@ {1, 2} to shave off one character. $\endgroup$
    – seismatica
    Aug 6, 2014 at 19:13
9
$\begingroup$
data // Query[{Keys, Values}]

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

$\endgroup$
4
  • $\begingroup$ Darn, why didn't I think of that. +1 $\endgroup$
    – Mr.Wizard
    Aug 6, 2014 at 20:49
  • 3
    $\begingroup$ 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). $\endgroup$ Aug 7, 2014 at 18:32
  • $\begingroup$ @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? $\endgroup$
    – Mr.Wizard
    Aug 7, 2014 at 20:02
  • $\begingroup$ data // Query[{Keys, Values}] doesn't work using version 10.1.0.0. $\endgroup$
    – Karsten7
    May 1, 2015 at 6:24
8
$\begingroup$

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)

$\endgroup$
2
  • $\begingroup$ Could you add {First /@ #, Last /@ #} &? $\endgroup$ Aug 7, 2014 at 21:08
  • $\begingroup$ @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.) $\endgroup$
    – Mr.Wizard
    Aug 7, 2014 at 21:18
4
$\begingroup$
{#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}}
$\endgroup$
2
$\begingroup$

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}}
$\endgroup$
5
  • $\begingroup$ 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. $\endgroup$ Aug 6, 2014 at 15:28
  • $\begingroup$ yep, you're absolutely right. $\endgroup$
    – gpap
    Aug 6, 2014 at 15:36
  • $\begingroup$ @Teake and gpap, you both forgot to localize your pattern names; use :> instead! $\endgroup$
    – Mr.Wizard
    Aug 6, 2014 at 20:51
  • $\begingroup$ @Mr.Wizard I claim a typo! $\endgroup$ Aug 6, 2014 at 20:54
  • $\begingroup$ done! thanks for the comment - this was written as hastily as they come. $\endgroup$
    – gpap
    Aug 7, 2014 at 8:44

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.