5
$\begingroup$

Sorry for the vague title, but I can't describe my problem properly and shortly.

I am maintaining a list of players and their games with the following form:

gamesPerPlayer = 
  {{playerId -> playerId1, games-> {game11, game12, game13}},
   {playerId -> playerId2, games-> {game21, game22, game23, game24}}}

Each game is a complex structure of nested lists but it has a unique field, so we can simply and think that each game is just {idGame, infoGame}. Thus my list is like this

gamesPerPlayer = 
  {{playerId -> playerId1, 
      games -> {{idGame11, infoGame11}, {idGame12, infoGame12}, {idGame13, infoGame13}}},
   {playerId -> playerId2, 
      games -> {{idGame21, infoGame21}, {idGame22, infoGame22}}}

How can I write a (fast) function to join two of this lists eliminating duplicates? The order of the playerId and the games in the new list is not important.

Just to clarify, an example:

list1 = {{playerId -> 1, game s-> {{11, "info"}, {12, "info"}}},
         {playerId -> 2, games->{{21, "info"}}}};
list2 = {{playerId -> 1, games -> {{11, "info"}, {13, "info"}}},
         {playerId -> 3, games -> {{31, "info"}}}};
newList = myJoinFunction[list1, list2];

Expected result:

{{playerId -> 1, games -> {{11, "info"}, {12, "info"}, {13, "info"}}},
 {playerId -> 2, games -> {{21, "info"}}, {playerId -> 3, games -> {{31, "info"}}}
$\endgroup$
  • 1
    $\begingroup$ Related: 16450 $\endgroup$ – Michael E2 Mar 16 '14 at 14:09
6
$\begingroup$

This looks a lot like the solution Yves came up with but I arrived at it independently:

combine[id_, tag_, rls__List] :=
 {id -> #[[1, 1]], tag -> Union @@ #[[All, 2]]} & /@
  GatherBy[{id, tag} /. Join[rls], First]

combine[playerId, games, list1, list2]
{{playerId -> 1, games -> {{11, "info"}, {12, "info"}, {13, "info"}}},
 {playerId -> 2, games -> {{21, "info"}}},
 {playerId -> 3, games -> {{31, "info"}}}}

Or, if you prefer, using Sow and Reap:

combine2[id_, tag_, rls__List] :=
  Last @ Reap[Sow @@@ ({tag, id} /. Join[rls]), _, {id -> #, tag -> Union @@ #2} &]

combine2[playerId, games, list1, list2]
{{playerId -> 1, games -> {{11, "info"}, {12, "info"}, {13, "info"}}},
 {playerId -> 2, games -> {{21, "info"}}},
 {playerId -> 3, games -> {{31, "info"}}}}
$\endgroup$
  • 1
    $\begingroup$ ... you took the time to do it properly, though. I confess to being a hardcoding bum. But it is Sunday and the sun is a-shining :D $\endgroup$ – Yves Klett Mar 16 '14 at 14:51
  • $\begingroup$ @Yves Then get off the computer and go play! :-) $\endgroup$ – Mr.Wizard Mar 16 '14 at 14:51
  • 1
    $\begingroup$ Beep beep zoom! $\endgroup$ – Yves Klett Mar 16 '14 at 14:53
6
$\begingroup$

Abstaining from using ReplaceAll:

{DeleteDuplicates[#[[1]]][[1]], 
   games -> 
    DeleteDuplicates@Flatten[#[[2, All, 2]], 1]} & /@ (Transpose /@ 
   GatherBy[Join[list1, list2], First])

Result:

(*{{playerId -> 1, games -> {{11, "info"}, {12, "info"}, {13, "info"}}},
   {playerId -> 2, games -> {{21, "info"}}},
   {playerId -> 3, games -> {{31, "info"}}}}*)
$\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.