I am working with lists of the following form:
data = {
<|"id" -> "name1", "p1" -> v1, "p2" -> v2, "p3" -> v3, "p4" -> v4|>,
<|"id" -> "name2", "p1" -> v1, "p2" -> v2, "p3" -> v3, "p4" -> v4|>,
<|"id" -> "name3", "p1" -> v1, "p2" -> v2, "p3" -> v3, "p4" -> v4|>,
<|"id" -> "name4", "p1" -> v1, "p2" -> v2, "p3" -> v3, "p4" -> v4|>
};
In essence, a list of associations where the keys are strings, and the values are all approximate real numbers except for the value associated with the "id"
key. Any of the values can be any approximate real number, i.e., simply because v1
is used in multiple associations doesn't mean that it is the same value in different associations. It could be the same, but most likely it won't be. Some of the values will be Indeterminate
. Also, although the number of properties in the above sample apart from the id is four, the associations could have any number of properties: I chose four properties simply for compactness.
My end goal is to take such lists and calculate things like Min
or Max
on various values and be able to return the entire record (association). I have come up with a general approach that works, however, my guess is that it is clunky, for it requires creating sub lists and doing substitutions. E.g., if I execute Max
as follows:
sampleList = {1., 2., Indeterminate, 3000.};
Position[sampleList, Max[sampleList]]
I get {{3}}
when what I expected was {{4}}
. So, for a function like Max
, I modify the example as follows so that it will give me what I expect:
sampleList = {1., 2., Indeterminate, 3000.} /. Indeterminate -> -\[Infinity];
Position[sampleList, Max[sampleList]]
and it gives the answer {{4}}
.
If I wanted to utilize Min
, I would modify the example like so:
sampleList = {1., 2., Indeterminate, 3000.} /. Indeterminate -> \[Infinity]
Position[sampleList, Min[sampleList]]
to get the expected answer of {{1}}
.
I know that there are many ways to solve things in Mathematica, but what I'm looking for is a solution to the problem that is more efficient and takes advantage of 'deeper' aspects of Mathematica so I can learn. What follows is the function I wrote for obtaining the record with the maximum value for any given property name:
ClearAll[GetMax];
GetMax::NoMax = "Position returned an empty list";
GetMax[data_List, key_String] := Module[
{pos, sub},
sub = (data[[All, key]] /. Indeterminate -> -\[Infinity]);
pos = Position[sub, Max[sub]];
If[0 == Length[pos], Return[$Failed, GetMax::NoMax]];
data[[pos[[1]]]]
];
Some sample data to drive the function:
data = {
<|"id" -> "id1", "p1" -> 2.33333, "p2" -> 2.25, "p3" -> 2.,"p4" -> 4, "p5" -> 0.433013, "p6" -> 1.1547|>,
<|"id" -> "id2", "p1" -> Indeterminate, "p2" -> 2., "p3" -> 2.,"p4" -> 10., "p5" -> 0., "p6" -> Indeterminate|>,
<|"id" -> "id3", "p1" -> 2.36391, "p2" -> 2.57143, "p3" -> 2.,"p4" -> 7, "p5" -> 0.728431, "p6" -> 0.859894|>,
<|"id" -> "id4", "p1" -> 1., "p2" -> 2.5, "p3" -> 2., "p4" -> 4,"p5" -> 0.5, "p6" -> 0.|>,
<|"id" -> "id5", "p1" -> 2.33333, "p2" -> 2.25, "p3" -> 2.,"p4" -> 4, "p5" -> 0.433013, "p6" -> 1.1547|>,
<|"id" -> "id6", "p1" -> 1.16667, "p2" -> 2.4, "p3" -> 2.,"p4" -> 5, "p5" -> 2.489898, "p6" -> 0.408248|>,
<|"id" -> "id7", "p1" -> Indeterminate, "p2" -> 2., "p3" -> 4.1,"p4" -> 6, "p5" -> 0., "p6" -> Indeterminate|>,
<|"id" -> "id8", "p1" -> 1.49308, "p2" -> 2.85714, "p3" -> 3.,"p4" -> 7, "p5" -> 0.832993, "p6" -> 0.27238|>,
<|"id" -> "id9", "p1" -> 1.5, "p2" -> 2.66667, "p3" -> 3.,"p4" -> 3, "p5" -> 0.471405, "p6" -> -0.707107|>,
<|"id" -> "id10", "p1" -> 1., "p2" -> 2.5, "p3" -> 2., "p4" -> 4,"p5" -> 0.5, "p6" -> 0.|>
};
Utilization of the function to retrieve the record associated with the maximum value of any given property:
GetMax[data, "p1"]
GetMax[data, "p2"]
GetMax[data, "p3"]
GetMax[data, "p4"]
GetMax[data, "p5"]
GetMax[data, "p6"]
It works, but as I stated at the outset, seems klunky compared to many of the elegant MMA solutions I see here on the forums. I also realize that GetMax
will have problems if the values are integers because Position
will potentially match on the FullForm
of \[Infinity]
, but that's a different problem!
Indeterminate -> +- Infinity
, you could do something likeSelect[sampleList, NumericQ]
before you look for max/min. Also, rather thanPosition[sub, Max[sub]]
you could usePositionLargest
/PositionSmallest
(after filtering out the non-numerics). $\endgroup$