2
$\begingroup$

Please can someone help with this question, I'm a bit stuck.

Write a function which will accept a list corresponding to a mark list, where each entry in the mark list has the form {test number, first name, family name, mark} or, if a mark is missing, {test number, first name, family name} and which will return a new list of consolidated marks where each list member has the form {first name, family name, test1_mark, test2_mark, ...} in alphabetical order of first names, for example, {{Barak, Obama, 22}, {..., ..., ...}, ...}.

Each list member should only contain test marks for tests attempted for each person (and so each member may have a different length). Your function should make use of a rule using appropriate patterns, and will probably need to use the Map and Rest functions and a function to sort by the first name.

marks = {{1, David, Cameron, 18}, {2, Donald, Trump, 8}, {3, David, Cameron, 11}, 
         {1, Barak, Obama, 22}, {1, Boris, Johnson}, {2, Boris, Johnson},
         {3, Boris, Johnson}, {4, David, Cameron, 17}};

So far I have created a rule, but it does not work on cases where there is no mark.

 list = {z_, x_, y_, v_} -> {x, y, v};
$\endgroup$
1
  • $\begingroup$ I think you can define two functions with the same name and with different numbers of arguments. For example: uFun[{z_,x_,y_,v_}]:={x,y,v} and uFun[{z_,x_,y_}]:={x,y}. Then uFun @ mark should make the trick. $\endgroup$ – vi pa Feb 6 '20 at 23:43
1
$\begingroup$

Not 100% clear what you want, but this will get you going.

Note that I use strings in the data.

marks = {{1, "David", "Cameron", 18}, {2, "Donald", "Trump", 8}, {3, 
"David", "Cameron", 11}, {1, "Barak", "Obama", 22}, {1, "Boris", 
"Johnson"}, {2, "Boris", "Johnson"}, {3, "Boris", "Johnson"}, {4, 
"David", "Cameron", 17}};

I assign a 'zero' if the test is listed but no grade given.

load[{tn_, fn_, ln_, grade_}] := {fn, ln, {tn, grade}};
load[{tn_, fn_, ln_}] := {fn, ln, {tn, 0}};

Run the load function, sort it, and GatherBy the first and last name.

db = GatherBy[Sort@Map[load,marks], #[[1]] && #[[2]] &];

{{{"Barak", "Obama", {1, 22}}}, {{"Boris", "Johnson", {1, 0}}, {"Boris", "Johnson", {2, 0}}, {"Boris", "Johnson", {3, 0}}}, {{"David", "Cameron", {1, 18}}, {"David", "Cameron", {3, 11}}, {"David", "Cameron", {4, 17}}}, {{"Donald", "Trump", {2, 8}}}}

Now group things together so it is {first_name, last_name,{{test,score},{test,score},...}

Map[{#[[1, 1]], #[[1, 2]], Transpose@# // Last} &, db]

{{"Barak", "Obama", {{1, 22}}}, {"Boris", "Johnson", {{1, 0}, {2, 0}, {3, 0}}}, {"David", "Cameron", {{1, 18}, {3, 11}, {4, 17}}}, {"Donald", "Trump", {{2, 8}}}}

$\endgroup$
2
$\begingroup$

Using marks from MikeY's answer:

assoc = KeySort @ GroupBy[PadRight[marks][[All, {2, 3, 1, 4}]],
   #[[;;2]] &, #[[All, 3 ;;]] &]

<|{"Barak", "Obama"} -> {{1, 22}},
{"Boris", "Johnson"} -> {{1, 0}, {2, 0}, {3, 0}},
{"David", "Cameron"} -> {{1, 18}, {3, 11}, {4, 17}},
{"Donald", "Trump"} -> {{2, 8}}|>

KeyValueMap[{## & @@ #, #2} &] @ assoc

{{"Barak", "Obama", {{1, 22}}},
{"Boris", "Johnson", {{1, 0}, {2, 0}, {3, 0}}},
{"David", "Cameron", {{1, 18}, {3, 11}, {4, 17}}},
{"Donald", "Trump", {{2, 8}}}}

Grid[%, Alignment -> {Left, Center}, Dividers -> {{True, False, {True}}, All}]

enter image description here

KeyValueMap[Apply[Join]@*List]@assoc // 
 Grid[#, Alignment -> {Left, Center}, Dividers -> {{True, False, {True}}, All}] &

enter image description here

Perhaps Dataset is a better approach to organize this sort of data:

ds1 = KeySort @ Dataset @ 
      GroupBy[PadRight[marks, Automatic, Missing[]][[All, {2, 3, 1, 4}]], 
       StringRiffle[#[[;; 2]]] &, 
       Association["tests" -> Association[ToString[#3] -> #4 & @@@ #]] &]

enter image description here

or, perhaps,

ds2 = KeySort /* Transpose /* Map[KeySort] @* Transpose @
  Dataset[GroupBy[PadRight[marks, Automatic, "n/a"][[All, {2, 3, 1, 4}]], 
     StringRiffle[#[[;; 2]]] &, 
     Association["test" <> ToString[#3] -> #4 & @@@ #] &]]

enter image description here

$\endgroup$
1
  • $\begingroup$ I’ve never gotten my head around Associations. Thanks for the answer. $\endgroup$ – MikeY Feb 7 '20 at 11:59
1
$\begingroup$

If you want a rule replacement, you could use this:

list = marks /. {{tn_, fn_, ln_, grade_} :> {fn, ln, {tn, grade}}, {tn_, fn_,
     ln_} :> {fn, ln, {tn, 0}}}

All other gathering and sorting as per @MikeY could then be applied.

Note that you want RuleDelayed ":>" rather than rule "->" because each time the replacement is evaluated, the values must change.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.