4

I have two list operations which I would like to ask for help. The way I implemented them is not very elegant, so I want to learn from you experts.

1) Suppose I have two lists, one is like {{0,2,4},{1,3,2},{2,0,4}} and the other is {{1,3,7},{2,4,6},{3,1,9}}. I want to either based on the value, or based on some criterion to filter through the first list, and then get the corresponding elements in the second. For example, based on value which is non-zero, I want to get {{3,7},{2,4,6},{3,9}}. Based on the condition greater than 2, I want to get {{7},{4},{9}}.

2) I have a list such as {{{1,2},{1,1}},{{1,3},{2,4}},{{1,2},{2,3}},{{1,4},{3,3}}}. I want to form {{{1,2},{{1,1},{2,3}}},{{1,3},{{2,4}}},{{1,4},{{3,3}}}. That is, I want to group those second lists if the first element is the same. How can I do this in a beautiful way?

Many thanks.

Michael Pilat
  • 6,480
  • 27
  • 30
Qiang Li
  • 10,593
  • 21
  • 77
  • 148

2 Answers2

11

For the first part, you want Pick:

In[27]:= Pick[{{1,3,7},{2,4,6},{3,1,9}},{{0,2,4},{1,3,2},{2,0,4}},_?Positive]
Out[27]= {{3,7},{2,4,6},{3,9}}

In[28]:= Pick[{{1,3,7},{2,4,6},{3,1,9}},{{0,2,4},{1,3,2},{2,0,4}},_?(#>2&)]
Out[28]= {{7},{4},{9}}

For the second question, GatherBy gets you most of the way there:

In[29]:= x = GatherBy[{{{1, 2}, {1, 1}}, {{1, 3}, {2, 4}}, {{1, 2}, 
    {2, 3}}, {{1, 4}, {3, 3}}}, First]

Out[29]= {{{{1, 2}, {1, 1}}, {{1, 2}, {2, 3}}}, {{{1, 3}, 
    {2, 4}}}, {{{1, 4}, {3, 3}}}}

And then you can apply a rule to clean things up a bit:

In[30]:= x /. l:{{a_, _}..} :> {a, Last /@ l}

Out[30]= {{{1, 2}, {{1, 1}, {2, 3}}}, {{1, 3}, {{2, 4}}},
    {{1, 4}, {{3, 3}}}}
Michael Pilat
  • 6,480
  • 27
  • 30
  • +1. thank you. for the first part, I thought about `Pick`. But it does not allow me to put a function there (not like `Select`), does it? How do you go about >2 case? – Qiang Li Mar 09 '11 at 05:58
  • I just updated my answer, you can use a condition on the pattern in the third argument. I just cleaned up my answer to the second part a tiny bit too... – Michael Pilat Mar 09 '11 at 05:59
  • 4
    @Qiang @Michael Even if Pick wouldn't have had the 3rd argument you could have done it with Pick as follows: `Pick[{{1, 3, 7}, {2, 4, 6}, {3, 1, 9}}, Positive@{{0, 2, 4}, {1, 3, 2}, {2, 0, 4}}]`. You could use pure functions in more complex cases. Note that you can use @ here because Positive is Listable. For non-Listable functions use Map at the appropriate level. – Sjoerd C. de Vries Mar 09 '11 at 10:35
3

As Michael said, Pick is definitely the way to go for the first one.

For the second part, I'd like to offer an alternative that lets you do this in one line: SelectEquivalents. (I know, rather self promoting, but I use this function a lot.) To get the result your looking for, simply enter

In[1] := SelectEquivalents[ <list>, First, Last, {#1,#2}& ]
Out[1]:= {{{1, 2}, {{1, 1}, {2, 3}}}, {{1, 3}, {{2, 4}}}, {{1, 4}, {{3, 3}}}}

Internally, SelectEquivalents uses Reap and Sow, so First tags each element in <list>, Last transforms the element into the form we wish to use, and {#1, #2}& returns a list with elements of the form {Tag, {<items with that tag>}}. The advantage is that you get to specify everything in one step getting you what you want without subsequent transformations.

Community
  • 1
  • 1
rcollyer
  • 10,475
  • 4
  • 48
  • 75