4
$\begingroup$

I have a two-dimensional list where each value of $ x $ can have many values of $ y $. for example

{{1, 2}, {1, 3}, {1, 4}, {2, 2}, {2, 3}, {2, 4}, {2, 5}}

For each value of $ x $, I have to find out the maximum value of $ y $. So, the result will be like

{{1, 4}, {2, 5}}

I tried with Max, Short but was not able to run this in the loop.

Is there an efficient method?

$\endgroup$

6 Answers 6

4
$\begingroup$

Using GroupBy and MaximalBy

gpLst = GroupBy[lst, First, MaximalBy[#, Last][[1]] &]

<|1 -> {{1, 4}}, 2 -> {{2, 5}}|>

gpLst//Values

{{1, 4}, {2, 5}}

$\endgroup$
1
  • $\begingroup$ Thanks @Anjan. It worked. $\endgroup$
    – D.Nanda
    Aug 8, 2020 at 6:09
6
$\begingroup$

Another one:

data = {{1, 2}, {1, 3}, {1, 4}, {2, 2}, {2, 3}, {2, 4}, {2, 5}};
GroupBy[data, First -> Last, Max]

<|1 -> 4, 2 -> 5|>

If you need the result as a list of pairs:

KeyValueMap[List, %]

{{1, 4}, {2, 5}}

$\endgroup$
1
  • $\begingroup$ Yes, it also worked. Thanks $\endgroup$
    – D.Nanda
    Aug 9, 2020 at 16:16
4
$\begingroup$

Since you specified "Is there an efficient method?"

GatherBy[Sort@list, First][[All, -1]]

(here list is your list or variable the list is assigned to).

This should outperform the accepted solution by at least an order of magnitude.

Slightly more verbose, but even quicker:

With[{g = GatherBy[list, First]}, 
 Transpose[{g[[All, 1, 1]], Max /@ g[[All, All, 2]]}]]
$\endgroup$
4
$\begingroup$

Here's a variation of @sjoerd's answer:

data={{1,2},{1,3},{1,4},{2,2},{2,3},{2,4},{2,5}};

ResourceFunction["GroupByList"][data[[All,2]],data[[All,1]],Max]

<|1 -> 4, 2 -> 5|>

For large datasets, this will be much faster than GroupBy:

data = RandomInteger[10000, {10^6, 2}];
r1=ResourceFunction["GroupByList"][data[[All,2]], data[[All,1]], Max];//AbsoluteTiming
r2=GroupBy[data, First->Last, Max];//AbsoluteTiming

r1===r2

{0.10384, Null}

{0.514609, Null}

True

This approach is also slightly faster than @ciao's.

$\endgroup$
3
$\begingroup$
list = {{1, 2}, {1, 3}, {1, 4}, {2, 2}, {2, 3}, {2, 4}, {2, 5}};
MaximalBy[#, Last] & /@ Split[list, #1[[1]] == #2[[1]] &]
$\endgroup$
2
$\begingroup$

Similar in spirit to Sjoerd Smit's answer: the wonderful thing about Mathematica's associations is that they automatically override earlier values in favor of later ones, which is exactly what you want! To demonstrate, let me shuffle your input:

data = {{1, 2}, {1, 4}, {2, 2}, {2, 4}, {2, 5}, {1, 3}, {2, 3}}

Converting into an association only keeps the last occurrence of each first coordinate:

AssociationThread @@ Transpose@data

returns <|1 -> 3, 2 -> 3|>. So we just need to sort first (fortunately Sort automatically checks second coordinates after it checks first coordinates):

AssociationThread @@ Transpose@Sort@data

returns <|1 -> 4, 2 -> 5|>. And if you want the answer as ordered pairs again, there's a function that does that:

KeyValueMap[List, AssociationThread @@ Transpose@Sort@data]

returns {{1, 4}, {2, 5}}.

$\endgroup$
1
  • $\begingroup$ Thank you @Greg $\endgroup$
    – D.Nanda
    Aug 9, 2020 at 16:13

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.