5

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"}}}
m_goldberg
  • 107,779
  • 16
  • 103
  • 257
José D.
  • 1,135
  • 10
  • 23

2 Answers2

6

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"}}}}*)
Yves Klett
  • 15,383
  • 5
  • 57
  • 124
6

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"}}}}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371