5
$\begingroup$

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},...}
$\endgroup$
2
  • 1
    $\begingroup$ Contries are the same for sure. $\endgroup$
    – David
    Aug 7, 2014 at 14:29
  • $\begingroup$ Related: (13886), (22599) $\endgroup$
    – Mr.Wizard
    Aug 7, 2014 at 16:05

8 Answers 8

4
$\begingroup$
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}}
*)
$\endgroup$
1
  • $\begingroup$ That's why taking l1 = {{"a", a1}, {"c", a3}, {"b", a2}}; would be a better example :) $\endgroup$
    – Öskå
    Aug 7, 2014 at 14:31
4
$\begingroup$

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

$\endgroup$
7
  • $\begingroup$ For small data it is a little faster to use L2[[Ordering[L2][[Ordering@Ordering@L1]]]] $\endgroup$ Aug 7, 2014 at 20:09
  • $\begingroup$ @Simon That's a lot of Ordering! Let me add it to the timings. :-) $\endgroup$
    – Mr.Wizard
    Aug 7, 2014 at 20:10
  • $\begingroup$ @Simon BenchmarkPlot doesn't seem to show much difference. Can you give me an example where the difference is more apparent? $\endgroup$
    – Mr.Wizard
    Aug 7, 2014 at 20:19
  • $\begingroup$ Hmm, I saw a fairly consistent difference for small n but after a restart it has gone away! $\endgroup$ Aug 7, 2014 at 20:42
  • 2
    $\begingroup$ Interesting! Now we need to find 10^5 countries to try it out. $\endgroup$ Aug 7, 2014 at 23:42
3
$\begingroup$
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}}

$\endgroup$
11
  • $\begingroup$ Unfortunately Sort[L2][[Ordering[L1]]] does not appear to work. $\endgroup$
    – Mr.Wizard
    Aug 7, 2014 at 16:40
  • $\begingroup$ @Mr.Wizard With me it produces the output shown in my answer. Please clarify $\endgroup$
    – eldo
    Aug 7, 2014 at 16:47
  • $\begingroup$ Actually I just remembered something; THIS does work: Sort[L2][[Ordering @ Ordering @ L1]], and I think it will help me find my elusive duplicate. $\endgroup$
    – Mr.Wizard
    Aug 7, 2014 at 16:52
  • $\begingroup$ @Mr.Wizard Thanks, I updated $\endgroup$
    – eldo
    Aug 7, 2014 at 17:00
  • 1
    $\begingroup$ @Mr.Wizard Perhaps the comment under this answer mathematica.stackexchange.com/a/31266/193 $\endgroup$ Aug 7, 2014 at 19:25
2
$\begingroup$
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

$\endgroup$
1
$\begingroup$

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

$\endgroup$
1
  • $\begingroup$ There's no need to extract the number from the position result: SortBy[l2, Position[l1, First@#]&] $\endgroup$ Aug 7, 2014 at 19:46
1
$\begingroup$

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]
$\endgroup$
0
$\begingroup$

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]}]
$\endgroup$
0
$\begingroup$

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}}*)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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