12
$\begingroup$

Assume I have two lists, for example the following:

list1 = {1, 2, 1, 3, 2};
list2 = {{1, a}, {3, c}, {2, b}};

I would like to compare each element of list1 with the first column in list2 and if there is a match, assign the element from column 2 of list2 to the corresponding index in a third list3.

In this case, list3 would looks like this:

list3 = {a, b, a, c, b}

I am rather new to Mathematica and I'm sure there is a function for exactly this, but I spent several hours with Map, Thread and similar functions and haven't found a solution yet.

$\endgroup$
1
  • $\begingroup$ Welcome to Mma SE. To get started: 1) take the introductory tour now, 2) when you see good questions and answers, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge, 3) remember to accept the answer, if any, that solves your problem, by clicking checkmark sign, 4) give help too, by answering questions in your areas of expertise. $\endgroup$
    – bmf
    Commented Jan 11, 2023 at 4:51

9 Answers 9

14
$\begingroup$

What about the simple?

list1 /. Apply[Rule, list2, {1}]

list

$\endgroup$
2
  • 1
    $\begingroup$ Worked flawlessly, thank you! $\endgroup$ Commented Feb 16, 2023 at 19:44
  • $\begingroup$ @mrkhlavkalash you're very welcome. Glad I was able to help :-) $\endgroup$
    – bmf
    Commented Feb 16, 2023 at 23:07
11
$\begingroup$

You can also use Lookup:

Lookup[Rule @@@ list2, list1]
{a, b, a, c, b}
$\endgroup$
5
  • 1
    $\begingroup$ Happy new year!!! $\endgroup$
    – bmf
    Commented Jan 11, 2023 at 6:55
  • 2
    $\begingroup$ For list1 = {1, 2, 1, 3, 2, 4, 5};, where some entries do not have a lookup value: Lookup[Rule @@@ list2, #, #] & /@ list1. $\endgroup$
    – Syed
    Commented Jan 11, 2023 at 7:49
  • 1
    $\begingroup$ Happy new year to you too @bmf. $\endgroup$
    – kglr
    Commented Jan 13, 2023 at 12:10
  • 1
    $\begingroup$ Thank you @Syed. $\endgroup$
    – kglr
    Commented Jan 13, 2023 at 12:10
  • $\begingroup$ @kglr On the doc page, there was nothing about Lookup being able to handle lists, only associations; so I wrote the Association version of the answer; and then your answer appeared. Later on I found that as a side note in the Details section. I hope the page can be updated. $\endgroup$
    – Syed
    Commented Jan 13, 2023 at 12:22
10
$\begingroup$

Using SequenceReplace:

SequenceReplace[list1, Join @@ Function[{x, y}, Thread[Rule[{{x}}, y]]] @@@ list2]
(*{a, b, a, c, b}*)
$\endgroup$
2
  • 1
    $\begingroup$ Very nice!!! 3 down and 7 remaining :-) $\endgroup$
    – bmf
    Commented Jan 11, 2023 at 5:41
  • 1
    $\begingroup$ New year, new game of the ten ways to code something :-) $\endgroup$ Commented Jan 11, 2023 at 5:44
9
$\begingroup$

This isn't so much an answer as an attempt at pedagogy. The most canonical answer here is the one from @bmf, but as it stands I fear the OP may not quite grok what's going on.

@mrkhlavkalash, you've asked the question in terms of columns and indices, but the key insight is to see that what you're asking for is the exact functionality provided by ReplaceAll (which is typically written using the syntactic sugar of /.. Here's an example of ReplaceAll:

{1, 2, 3, 1} /. {1 -> "A", 3 -> XYZ}

Result: {"A", 2, XYZ, "A"}

Hopefully, that shows clearly how it works. Notice the 2 didn't get replaced, because there was no rule for 2. Each occurrence of 1 was replaced with "A" and each occurrence of 3 was replaced with the raw symbol XYZ. Our rules are specified with -> (which is just syntactic sugar for Rule.

So, in your case, what we want is to end up with this expression somehow:

{1, 2, 1, 3, 2} /. {1 -> a, 3 -> c, 2 -> b}

You're already using a variable to hold the target list, so we really just need

list1 /. {1 -> a, 3 -> c, 2 -> b}

How can we transform list2? Well, {1,a} is an expression with head List, and we want an expression with head Rule. There is a way to replace a head, and it's called Apply (and yet again, there is a syntac sugar-y form @@. So,

Rule @@ {1, a}

gives 1 -> a, aka Rule[1,a].

But we need to do this for all of the pairs in list2. This can be done with MapApply (aka @@@). So,

Rule @@@ list2

gives {1 -> a, 3 -> c, 2 -> b}. This is exactly what we need. Putting it together,

list1 /. Rule @@@ list2

which gives {a, b, a, c, b}.

Okay, I pulled a bit of sleight of hand, because @bmf actually offered

list1 /. Apply[Rule, list2, {1}]

I already discussed Apply, aka @@, but you can provide an optional argument to specify the level at which to do the Apply-ing, and that's what the {1} is doing there. We're applying Rule to every element at level 1, and only level 1, in list2.

It's often fun to see how many different ways something can be accomplished in Mathematica, so I offered something slightly different.

$\endgroup$
1
  • 1
    $\begingroup$ that's a wonderful explanation and kudos for taking the time to write it down! $\endgroup$
    – bmf
    Commented Jan 11, 2023 at 6:08
9
$\begingroup$

another possibility (8 more left)

Last@Reap@Scan[Sow@First@Cases[list2, {#, x_} :> x] &, list1]

Mathematica graphics

Scan is useful function. It applies a function to each element in a list. So useful for doing same thing to each element of a list. But note that with Scan need to use Sow in order to accumulate the result. It does not do it automatically. That is why Sow and Reap are needed above.

Update

Here is version with Flatten to give same result as all the other answers. I do not like very long commands, so I broke the above into 2 lines

Reap@Scan[Sow@Cases[list2, {#, x_} :> x] &, list1];
Flatten@Last[%]

Mathematica graphics

$\endgroup$
2
  • 1
    $\begingroup$ (+1) with a very minor comment that you can do a last Flatten. The other 8 ways are left as an exercise to the interested reader :-) $\endgroup$
    – bmf
    Commented Jan 11, 2023 at 5:02
  • 3
    $\begingroup$ @bmf Yes, sure an extra Flatten to remove one of the { will be needed but I wanted to make sure it all fit on one line, and Flatten is a long command so I removed it :), it is still not as short as your solution ofcourse. $\endgroup$
    – Nasser
    Commented Jan 11, 2023 at 5:05
8
$\begingroup$

Here I have added two entries for which lookup is not present; these are included without change.

list1 = {1, 2, 1, 3, 2, 4, 5};
list2 = {{1, a}, {3, c}, {2, b}};

From first principles:

If[Position[list2, #, {2}] == {}, #, 
   Extract[list2, First@Position[list2, #, {2}] + {0, 1}]] & /@ list1

OR

SubstitutionSystem[Rule @@@ list2, list1]

which is redundant and overkill.


Result:

{a, b, a, c, b, 4, 5}

$\endgroup$
6
$\begingroup$

Using dict/map also works. In Mathematica, it's <| |>

{{1, a}, {3, c}, {2, b}} //
Map[Apply[Rule]] //
Association //
Map //
# @ {1, 2, 1, 3, 2}&
$\endgroup$
3
$\begingroup$

Using ReplaceAt (new in V 13.1)

list1 = {1, 2, 1, 3, 2};
list2 = {{1, a}, {3, c}, {2, b}};

ReplaceAt[list1, Rule @@@ list2, All]

{a, b, a, c, b}

Why would we use ReplaceAt instead of the simpler ReplaceAll ?

list1 /. Rule @@@ list2

{a, b, a, c, b}

Because we can easily restrict the replacement(s) using the usual position syntax:

ReplaceAt[list1, Rule @@@ list2, 2 ;; -2]

{1, b, a, c, 2}

ReplaceAt[list1, Rule @@@ list2, {{2}, {4}}]

{1, b, 1, c, 2}

$\endgroup$
3
$\begingroup$

For the specific example given by the OP

FromLetterNumber[list1]

(* {a,b,a,c,b} *) 

Just for fun:

FromLetterNumber[list1,#]&/@{"English",Greek","Cyrillic",
  "Arabic","Russian","Devanagari",
  "Polish"}

{{a,b,a,c,b},{{α,β,α,γ,β}},{а,б,а,в,б},
 {ا,ب,ا,ت,ب},{а,б,а,в,б},{अ,आ,अ,इ,आ},
 {a,ą,a,b,ą}} 
$\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.