5

For example I have two lists of such structure

L1 = {{"Australia",a1}, {"USA",a2}, {"Norway",a3},...}
L2 = {{"Russia",b1}, {"Norway",b2}, {"Japan",b3},...}

I would like to sort countries of the list L2 in the countries' order of L1 to compare it entirely (I mean the values matching for each country). Thus I would like to gain something resembling this as a result where L3 is the sorted L2:

L1 = {{"Australia",a1}, {"USA",a2}, {"Norway",a3},...}
L3 = {{"Australia",b26},{"USA",b13} {"Norway",b2},...}
Öskå
  • 8,587
  • 4
  • 30
  • 49
David
  • 165
  • 3

8 Answers8

4
l1 = {{"a", a1}, {"b", a2}, {"c", a3}}
l2 = {{"c", b3}, {"b", b2}, {"a", b1}}
{#, # /. Rule @@@ l2} &[l1[[All, 1]]] // Transpose
(*
   {{"a", b1}, {"b", b2}, {"c", b3}}
*)
Dr. belisarius
  • 115,881
  • 13
  • 203
  • 453
4

Edit: This question closely related to one asked on Stack Overflow before the existence of Mathematica.SE. Please see this link for the history and examples:

See also:

The first Q&A references a use of the Ordering method shown below from 2007 on MathGroup:

(Thanks to belisarius for helping me find this.)


From your declaration "Contries(sic) are the same for sure." follows:

L1 = {{"IsleOfMan", 1}, {"Samoa", 2}, {"Tunisia", 3}, {"Svalbard", 4}, {"NewCaledonia", 5}};
L2 = {{"Tunisia", 1}, {"NewCaledonia", 2}, {"IsleOfMan", 3}, {"Svalbard", 4}, {"Samoa", 5}};

GatherBy[L1 ~Join~ L2, First][[All, 2]]
{{"IsleOfMan", 3}, {"Samoa", 5}, {"Tunisia", 1}, {"Svalbard", 4}, {"NewCaledonia", 2}}

Or correcting eldo's attempt as using Ordering:

Sort[L2][[Ordering @ Ordering @ L1]]
{{"IsleOfMan", 3}, {"Samoa", 5}, {"Tunisia", 1}, {"Svalbard", 4}, {"NewCaledonia", 2}}

Both are much faster than belisarius's method, with Ordering being particularly good:

Edit: Now including Simon Woods' recommendation from the comments as sim.

bel[{l1_, l2_}] := {#, # /. Rule @@@ l2} &[l1[[All, 1]]] // Transpose

wiz[{L1_, L2_}] := GatherBy[L1 ~Join~ L2, First][[All, 2]]

ord[{L1_, L2_}] := Sort[L2][[Ordering @ Ordering @ L1]];

sim[{L1_, L2_}] := L2[[ Ordering[L2][[Ordering @ Ordering @ L1]] ]]

gen[n_] := Table[{RandomSample @ Range @ n, Range @ n}\[Transpose], {2}]

Needs["GeneralUtilities`"]

BenchmarkPlot[{bel, wiz, ord, sim}, gen, 2^Range[20],
  "IncludeFits" -> True, ImageSize -> 500, PlotRange -> {Automatic, {1*^-6, 1}}]

enter image description here

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • For small data it is a little faster to use L2[[Ordering[L2][[Ordering@Ordering@L1]]]] – Simon Woods Aug 07 '14 at 20:09
  • @Simon That's a lot of Ordering! Let me add it to the timings. :-) – Mr.Wizard Aug 07 '14 at 20:10
  • @Simon BenchmarkPlot doesn't seem to show much difference. Can you give me an example where the difference is more apparent? – Mr.Wizard Aug 07 '14 at 20:19
  • Hmm, I saw a fairly consistent difference for small n but after a restart it has gone away! – Simon Woods Aug 07 '14 at 20:42
  • 2
    Interesting! Now we need to find 10^5 countries to try it out. – Dr. belisarius Aug 07 '14 at 23:42
  • @belisarius LOL -- touche. I always take examples and merely that, assuming application to other, larger problems, but if the OP really only wants this for working with country names any of these methods will do fine. – Mr.Wizard Aug 08 '14 at 00:08
  • T'was a joke, of course. I didn't worry about the performance just because I was thinking narrowly. It's always better to be able to see the forest. +1 – Dr. belisarius Aug 08 '14 at 00:43
3
L1 = {{"Australia", a1}, {"USA", a2}, {"Norway", a3}};
L2 = {{"USA", b1}, {"Norway", b2}, {"Australia", b3}};

L2[[Flatten[Position[First /@ L2, #] & /@ First /@ L1]]]

{{"Australia", b3}, {"USA", b1}, {"Norway", b2}}

Or (revised as per Mr. Wizard's comments)

Sort[L2][[Ordering @ Ordering @ L1]]

{{"Australia", b3}, {"USA", b1}, {"Norway", b2}}

eldo
  • 67,911
  • 5
  • 60
  • 168
  • Unfortunately Sort[L2][[Ordering[L1]]] does not appear to work. – Mr.Wizard Aug 07 '14 at 16:40
  • @Mr.Wizard With me it produces the output shown in my answer. Please clarify – eldo Aug 07 '14 at 16:47
  • Actually I just remembered something; THIS does work: Sort[L2][[Ordering @ Ordering @ L1]], and I think it will help me find my elusive duplicate. – Mr.Wizard Aug 07 '14 at 16:52
  • @Mr.Wizard Thanks, I updated – eldo Aug 07 '14 at 17:00
  • I still can't find the duplicate; the only use of double Ordering I can find at the moment is this. – Mr.Wizard Aug 07 '14 at 17:06
  • 1
    @Mr.Wizard Perhaps the comment under this answer http://mathematica.stackexchange.com/a/31266/193 – Dr. belisarius Aug 07 '14 at 19:25
  • @belisarius Thanks! If I had bothered to take a closer look at the linked comment I would have seen the link to this: (4545223) which is exactly what I was looking for! I have to remember that some of these questions were from that long ago. No point in looking for the duplicate on this site! – Mr.Wizard Aug 07 '14 at 19:42
  • @belisarius Since you answered this question yourself so long ago I'm a little disappointed you didn't use that method here. I guess no one is perfect. :^) – Mr.Wizard Aug 07 '14 at 19:52
  • @Mr.Wizard The only Perfect one is Ford. You're welcome. – Dr. belisarius Aug 07 '14 at 19:55
  • @Mr.Wizard BTW, Did you delete your question about incompabilities between V7 and V10? Can't find it anymore – eldo Aug 07 '14 at 22:11
  • @eldo It's still there: (56728). The title is unchanged but I did edit the bold line in the body; perhaps you were searching on a phrase in that. You can mark a question as a favorite if you worry about it getting lost; that way it will always be there in your profile even if the title or body are edited. – Mr.Wizard Aug 07 '14 at 23:38
2
L1 = {{"Australia", a1}, {"Norway", a2}, {"USA", a3}, {"Russia", a4}, {"Japan", a5}} ;
L2 = {{"Russia", b1}, {"Norway", b2}, {"Japan", b3}};

x = First /@ L1;

y = Cases[L2, {#, _}] & /@ x;

z = Apply[Sequence, y, {2}] /. {} -> "";

Grid[Prepend[Transpose[{L1, z}], {"L1", "L3"}], Frame -> All]

enter image description here

Edit

For speed, create y like so :--

getSubset[input_List, sub_List] := Module[{test},
  (test@# = True) &~Scan~sub;
  Apply[Sequence, Reap[Cases[input, x : {y_?test, ___} :> x~Sow~y], sub][[2]], {2}]] 

y = getSubset[L2, x]

Timing Tests

test = Range[10000];
L1 = Transpose[{test, test}];
L2 = RandomSample[L1, 10000];

Row[{First@Timing[x = First /@ L1;
    y = Cases[L2, {#, _}] & /@ x;
    z = Apply[Sequence, y, {2}] /. {} -> ""], " seconds"}]

46.457098 seconds

Row[{First@Timing[x = First /@ L1;
    y = getSubset[L2, x];
    z = Apply[Sequence, y, {2}] /. {} -> ""], " seconds"}]

0.124801 seconds

Row[{First@Timing[
    {#, # /. Rule @@@ L2} &[L1[[All, 1]]]], " seconds"}]

1.372809 seconds

Row[{First@Timing[
    L2[[Flatten[Position[First /@ L2, #] & /@ First /@ L1]]]], " seconds"}]

5.163633 seconds

Chris Degnen
  • 30,927
  • 2
  • 54
  • 108
1

SortBy[l2, Position[l1, #1 [[1]]] [[1, 1]] &]

andre314
  • 18,474
  • 1
  • 36
  • 69
1

While there are a number of replies with ways of ordering the two lists, let me give a different approach to comparing two sets of data of the form you gave. This applies if you are using v10 as it uses Associations.

Here's the data eldo used

l1 = {{"Australia", a1}, {"USA", a2}, {"Norway", a3}};
l2 = {{"USA", b1}, {"Norway", b2}, {"Australia", b3}};

Construct two Associations:

{assoc1, assoc2} = (Association[Rule @@@ #] & /@ {l1, l2})

(*
{<|"Australia" -> a1, "USA" -> a2, "Norway" -> a3|>, <|"USA" -> b1, 
  "Norway" -> b2, "Australia" -> b3|>}
*)

The advantage is that you can now use the countries as keys, and there is no need to worry about ordering. Here are the keys:

Keys@assoc1
(*{"Australia", "USA", "Norway"}*)

Here is one way to check all of them for equality:

{#, assoc1[#] == assoc2[#]} & /@ Keys[assoc1]
acl
  • 19,834
  • 3
  • 66
  • 91
0

Using Mathematica 10's associations makes this simple.

l1 = {{"Australia", a1}, {"USA", a2}, {"Norway", a3}, {"Russia", a4}, {"Japan", a5}};

Convert the second list to an association

l2 = Association@((#[[1]] -> #[[2]]) & /@ {{"Russia", b1}, {"Norway", b2}, {"Japan", b3}, {"Australia", b4}, {"USA", b5}});

Apply the association rules

l3 = ReplaceAll[l1, {x_, y_} :> {x, l2[x]}]
John McGee
  • 2,538
  • 11
  • 15
0

Lookup is working also.

    l1 = {{"Australia", a1}, {"USA", a2}, {"Norway", a3}};
    l2 = {{"USA", b1}, {"Norway", b2}, {"Australia", b3}};
    {First@#, Lookup[Rule @@@ l2, First@#]} & /@ l1

or

    {#, Lookup[Rule @@@ l2, #]} & /@ l1[[;; , 1]]

    (*{{"Australia", b3}, {"USA", b1}, {"Norway", b2}}*)
Basheer Algohi
  • 19,917
  • 1
  • 31
  • 78