# Manipulating a list in order to make a certain calculation

I have following list

list =
{{{1 -> 3, 2 -> 1, 3 -> 2}, {1 -> 6, 2 -> 7}, {1 -> 5}},
{{1 -> 7, 2 -> 8, 3 -> 6, 4 -> 9, 5 -> 3}, {1 -> 1, 2 -> 5, 3 -> 3, 4 -> 2},
{1 -> 6}},
{{1 -> 2, 2 -> 4}, {1 -> 4}, {1 -> 3, 2 -> 5, 3 -> 3}}}


I evaluated

list1 = Values[list]


and got

{{{3, 1, 2}, {6, 7}, {5}},
{{7, 8, 6, 9, 3}, {1, 5, 3, 2}, {6}},
{{2,4}, {4}, {3, 5, 3}}}


Now I want to sort all the elements from each innermost sublist from higher to lower values and and take ratios from 2nd element onward of each sublist with the first element. When a sublist has only one element then the ratio will be zero.

• Can you how the expected output for your example? Commented Nov 21, 2017 at 20:41

Not sure how many elements you want in your final array, but try this.

list = {{{1 -> 3, 2 -> 1, 3 -> 2}, {1 -> 6,
2 -> 7}, {1 -> 5}}, {{1 -> 7, 2 -> 8, 3 -> 6, 4 -> 9,
5 -> 3}, {1 -> 1, 2 -> 5, 3 -> 3, 4 -> 2}, {1 -> 6}}, {{1 -> 2,
2 -> 4}, {1 -> 4}, {1 -> 3, 2 -> 5, 3 -> 3}}};

list1 = Values[list];


First the sort.

list2 = list1
list3 = list1

For[i = 1, i <= Length[list1], i++,
For[j = 1, j <= Length[list1[[i]]], j++,
list2[[i, j]] = SortBy[list1[[i, j]], -N[#] &]]]


Now the sublists are sorted in list2.

list2
(* {{{3,2,1},{7,6},{5}},{{9,8,7,6,3},{5,3,2,1},{6}},{{4,2}{4},{5,3,3}}} *)


Get the ratios.

For[i = 1, i <= Length[list1], i++,
For[j = 1, j <= Length[list1[[i]]], j++,
For[k = 1, k <= Length[list2[[i, j]]], k++,
If[Length[list2[[i, j]]] == 1, list3[[i, j, k]] = 0,
list3[[i, j, k]] = list2[[i, j, k]]/list2[[i, j, 1]]
]]]]


Final array in list3

(* {{{1,2/3,1/3},{1,6/7},{0}},{{1,8/9,7/9,2/3,1/3},{1,3/5,2/5,1/5},{0}},{{1,1/2},{0},{1,3/5,3/5}}} *)

• Wow, thank you so much for the help. your help is appreciated. Commented Nov 22, 2017 at 19:42

Consider list1 to be the output obtained from evaluating Values[list], as displayed in the question.

After evaluating

Apply[
Through[{First, Rest}[Reverse[Sort[{##}]]]] &,
list1, {2}]


we obtain

{
{{3, {2, 1}}, {7, {6}}, {5, {}}},
{{9, {8, 7, 6, 3}}, {5, {3, 2, 1}}, {6, {}}},
{{4, {2}}, {4, {}}, {5, {3, 3}}}
}


Apply allows us to apply a function on every sub-list in list1 ie the output of Apply[f[{##}]&,list1,{2}] is:

{
{f[{3, 1, 2}], f[{6, 7}], f[{5}]},
{f[{7, 8, 6, 9, 3}], f[{1, 5, 3, 2}], f[{6}]},
{f[{2, 4}], f[{4}], f[{3, 5, 3}]}
}


Instead of applying a generic function like f[{##}]& we need to Sort the entries of each list and separate the first item from the rest of the list. This is achieved with substituting

Through[{First, Rest}[Reverse[Sort[{##}]]]] &


for our generic f.

Note how, this function first Sorts the entries of each list in ascending order (default), then it Reverses the output of Sort (in order to get the descending sort that is required).

Finally we use Through to obtain a list of {First[#],Rest[#]}& items of each sub-list.

As an aside, consider the effect of the preceding function on the first sub-list ie evaluate {First[#],Rest[#]}&@{3, 1, 2}. The output is, as required, {3,{1,2}}.

The pairs of properly sorted and separated sub-lists can now be manipulated to produce the required ratios (see question) by applying the following rules

{{n_, {}} -> {0}, {n_, l_} :> l/n}


The first rule will take care of every sub-list with a single entry, while the second rule will produce the required ratios, without risking division-by-{} unwanted output, since that danger is removed by the application of the first rule.

The notebook code along with the output is given below

Apply[
Through[{First, Rest}[Reverse[Sort[{##}]]]] &,
list1, {2}] /.{
{n_, {}} -> {0},
{n_, l_} :> l/n
}


The output of evaluating the code above is

{
{{2/3, 1/3}, {6/7}, {0}},
{{8/9, 7/9, 2/3, 1/3}, {3/5, 2/5, 1/5}, {0}},
{{1/2}, {0}, {3/5, 3/5}}
}

• Awesome. Thank you for giving all the explanation with the solution. This is my answer. Thank you so much. Commented Nov 22, 2017 at 19:42

As an exercise in using postfix notation:

Clear["Global*"];
list = {{{1 -> 3, 2 -> 1, 3 -> 2}, {1 -> 6,
2 -> 7}, {1 -> 5}}, {{1 -> 7, 2 -> 8, 3 -> 6, 4 -> 9,
5 -> 3}, {1 -> 1, 2 -> 5, 3 -> 3, 4 -> 2}, {1 -> 6}}, {{1 -> 2,
2 -> 4}, {1 -> 4}, {1 -> 3, 2 -> 5, 3 -> 3}}};

res = list //
Values //
Map[ReverseSort, #, {2}] & //
Map[Rest[#/First@#] &, #, {2}] & //
Replace[#, {} -> {0}, {2}] &
`

{{{2/3, 1/3}, {6/7}, {0}}, {{8/9, 7/9, 2/3, 1/3}, {3/5, 2/5, 1/ 5}, {0}}, {{1/2}, {0}, {3/5, 3/5}}}