Find second largest elements in list

One way, not highly efficient:

lis = {37.21, 37.21, 37.2, 44, 44, 44, 101, 101};

lis ~Cases~ Union[lis][[-2]]
{44, 44, 44}

This should be a bit more efficient:

ConstantArray @@ Sort[Tally@lis][[-2]]

Caveat: both of these methods rely on sorting and therefore require numeric data.


flinty's method with refinements by both C. E. and me:

Pick[lis, lis, RankedMax[DeleteDuplicates@lis, 2]]

This appears to be the fastest overall and it avoids the sorting issue referenced above.


Benchmarking

A quick test of the methods posted so far reveals an interesting pattern. Note that in the benchmark I use a list of a fixed length of one million and vary the number of unique elements within that list.

Adding methods f5, f6, and f7, and a second test with unpackable data.

Performed in Mathematica 10.1

Needs["GeneralUtilities`"]

SetOptions[Benchmark, TimeConstraint -> 30];

f1[lis_] := lis ~Cases~ Union[lis][[-2]]
f2[lis_] := ConstantArray @@ Sort[Tally@lis][[-2]]
f3[lis_] := MaximalBy[DeleteCases[lis, Max@lis], # &] (* Conor/kglr *)
f4[lis_] := Split[Sort@lis][[-2]]  (* kglr *)
f5[lis_] := Pick[lis, lis - RankedMax[DeleteDuplicates@lis, 2], 0]; (* flinty/C. E. *)
f6[lis_] := Extract[List/@KeySort[PositionIndex[lis]][[-2]]][lis] (* CA Trevillian *)
f7[lis_] := Pick[lis, lis, RankedMax[DeleteDuplicates@lis, 2]] (* flinty/C.E./me *)

BenchmarkPlot[{f1, f2, f3, f4, f5, f6, f7},
  RandomInteger[#, 1*^6] &, 10^Range[6], Joined -> True]

BenchmarkPlot[{f1, f2, f3, f4, f5, f6, f7},
  Prepend[0.5]@RandomInteger[#, 1*^6] &, 10^Range[6], Joined -> True]

enter image description here

enter image description here


another way...

MaximalBy[DeleteCases[lis, Max@lis], # &]
{44, 44, 44}

Split[ Sort @ lis][[-2]]
 {44, 44, 44}

Also

Nearest[DeleteCases[Max @ #] @ #, Max @ #] & @ lis
{44, 44, 44}