Problem statement
The following challenge was recently posted to the J language programming forum by Skip Cave (http://jsoftware.com/pipermail/programming/2016-March/044561.html):
"The animal control officer wants to keep track of the animals in a city. The officer tags all the animals with a serial number. He keeps a list of all the animals he has tagged, along with each serial number."
In Mathematica form, the given list is:
data = {{"bird", 1}, {"cow", 2}, {"cow", 3}, {"dog", 4}, {"cow", 5},
{"cow", 6}, {"cow", 7}, {"bird", 8}, {"dog", 9}, {"cat", 10},
{"dog", 11}, {"pig", 12}, {"dog", 13}, {"bird", 14}, {"pig", 15}};
"The officer now wants to make a second list showing each type of animal along with all the serial numbers tagged for each animal."
In Mathematica form, the desired output is:
{{"bird", {1, 8, 14}}, {"cow", {2, 3, 5, 6, 7}},
{"dog", {4, 9, 11, 13}}, {"cat", {10}}, {"pig", {12, 15}}}
The challenge is to transform the given input data into that output form in as simple a way as possible. (As phrased in the J Programming forum post, the challenge was to do so in as short an expression as possible.)
My solution so far:
creatures = Union[First /@ data]
gathered = GatherBy[data, First]
serials = Map[Last, #] & /@ gathered
Transpose[{creatures, serials}]
Naturally, multiple steps may be combined, e.g.:
serials = Map[Last, #] & /@ GatherBy[data, First]
In fact, all steps could be combined into one long expression:
Transpose[{Union[First /@ data], Map[Last, #] & /@ GatherBy[data, First]}]
Moreover, the list serials
of lists of serial numbers could be formed using patterned replacement:
serials = gathered /. {animal_, n_Integer} -> n
Question
Is there some significantly shorter or simpler way to do this?
Notes
The original data and desired output could have been cast into the form of associations, but I suspect the operations required to go from data to output would be essentially the same.
Probably the output should be fed into
SortBy[#, First]&
so as to rearrange the output list to be sorted alphabetically by animal name.
P.S.
Can you suggest a better, but succinct, title for my post?
Normal[GroupBy[data, First -> Last]] /. Rule -> List
? $\endgroup$ – J. M.'s ennui♦ Mar 4 '16 at 18:04Association
as an intermediate structure. $\endgroup$ – murray Mar 4 '16 at 18:19List@@@Normal@GroupBy[data, First -> Last]
$\endgroup$ – MarcoB Mar 4 '16 at 18:53