4
$\begingroup$

I have the following lists:

l1 = {{"letter.a.a", "1"}, {"letter.b.b", "1"}, {"letter.c.c", 
    "1"}, {"letter.d.d", "1"}, {"letter.e.e", "1"}, {"letter.f.f", 
    "1"}};
l2 = {{"f", 10}, {"c", 9}, {"e", 8}, {"b", 7}, {"d", 6}, {"a", 5}};

I want to replace the integers in l2 with "1"s in l1 appropriately such that I get:

{{"letter.a.a", 5}, {"letter.b.b", 7}, {"letter.c.c", 
        9}, {"letter.d.d", 6}, {"letter.e.e", 8}, {"letter.f.f", 
        10}}

Given the strings in l1 and l2 are slightly different I wonder how one can do this?

Update: In a more complex system I have:

l1 = {{"Food.ALMOND.ALMONDCINNAMON", 
   "1"}, {"Food.BLACK_CURRANT.BLACK_CURRANTCORN", 
   "1"}, {"Food.BELL_PEPPER.BELL_PEPPERLIME", "1"}}
l2 = {{"BLACK_CURRANTCORN", 4}, {"BELL_PEPPERLIME", 
   3}, {"ALMONDCINNAMON", 2}}

Here strings are words that need to be separated. How would one solve it for such lists? I think StringTake method fails for this.

$\endgroup$

7 Answers 7

4
$\begingroup$
assoc = Association[Rule @@@ l2];
l3 = l1;
l3[[All, 2]] = assoc /@ StringTake[l1[[All, 1]], -1] ;
l3

{{"letter.a.a", 5}, {"letter.b.b", 7}, {"letter.c.c", 9}, {"letter.d.d", 6}, {"letter.e.e", 8}, {"letter.f.f", 10}}

Alternatively,

l3 = l1;
l3[[All, 2]] = assoc /@ StringSplit[l1[[All, 1]], "."][[All, -1]];
l3

same result

Update:

ClearAll[f]
f = Module[{assoc = Association[Rule @@@ #2], l3 = #}, 
    l3[[All, 2]] = assoc /@ StringSplit[l3[[All, 1]], "."][[All, -1]];l3] &;

l1b = {{"Food.ALMOND.ALMONDCINNAMON", "1"}, {"Food.BLACK_CURRANT.BLACK_CURRANTCORN", "1"}, 
  {"Food.BELL_PEPPER.BELL_PEPPERLIME", "1"}};
l2b = {{"BLACK_CURRANTCORN", 4}, {"BELL_PEPPERLIME", 3}, {"ALMONDCINNAMON", 2}};


f[l1, l2]

{{"Food.ALMOND.ALMONDCINNAMON", 2}, {"Food.BLACK_CURRANT.BLACK_CURRANTCORN", 4}, {"Food.BELL_PEPPER.BELL_PEPPERLIME", 3}}

$\endgroup$
3
  • $\begingroup$ I see, so the main method is to use StringTake, but what if the system is more complex? i.e. suppose we have words. Let me make a edit for more generalised case. $\endgroup$
    – Wiliam
    Commented Jan 15, 2019 at 16:58
  • $\begingroup$ I just updated the question, please see. $\endgroup$
    – Wiliam
    Commented Jan 15, 2019 at 17:04
  • 1
    $\begingroup$ @William, updated. $\endgroup$
    – kglr
    Commented Jan 15, 2019 at 17:17
3
$\begingroup$

Just for fun, Another possibility might be

l1[[All, 2]]=Flatten[StringCases[First@#,"letter." ~~ c_ :> c]& /@ l1] /. (Rule @@@ l2)

which make l1 as

{{"letter.a.a", 5}, {"letter.b.b", 7}, {"letter.c.c", 
  9}, {"letter.d.d", 6}, {"letter.e.e", 8}, {"letter.f.f", 10}}

I think @kglr using association looks more direct.

$\endgroup$
2
$\begingroup$

Just for fun also :)

Map[{#[[1, 1]], #[[2, 2]]} &, GatherBy[Flatten[{l1, l2}, 1], (Last@StringSplit[#[[1]], "."]) &]]
$\endgroup$
2
$\begingroup$
a = 
 {{"letter.a.a", "1"}, {"letter.b.b", "1"}, {"letter.c.c", "1"}, 
  {"letter.d.d", "1"}, {"letter.e.e", "1"}, {"letter.f.f", "1"}};

b = {{"f", 10}, {"c", 9}, {"e", 8}, {"b", 7}, {"d", 6}, {"a", 5}};

Using SubsetMap (new in 12.0)

SubsetMap[
 Lookup[
   Rule @@@ b,
   StringPart[First /@ a, -1]] &,
 a,
 {All, -1}]

returns

{{"letter.a.a", 5}, {"letter.b.b", 7}, {"letter.c.c", 9}, 
 {"letter.d.d", 6}, {"letter.e.e", 8}, {"letter.f.f", 10}}
$\endgroup$
2
$\begingroup$
l1 = 
 {{"letter.a.a", "1"}, {"letter.b.b", "1"}, {"letter.c.c", "1"}, 
  {"letter.d.d", "1"}, {"letter.e.e", "1"}, {"letter.f.f", "1"}};

l2 = {{"f", 10}, {"c", 9}, {"e", 8}, {"b", 7}, {"d", 6}, {"a", 5}};

Using Thread:

(s |-> {First@s[[1]], Last@s[[-1]]}) /@ Thread[{l1, SortBy[l2, First]}]

(*{{"letter.a.a", 5}, {"letter.b.b", 7}, {"letter.c.c", 9},
   {"letter.d.d", 6}, {"letter.e.e", 8}, {"letter.f.f", 10}}*)

The above works as long as the list l1 is in the order provided by the author of the OP. Now suppose that the list l1 is not in that order, that is, it is in the following order:

l1 = 
  {{"letter.f.f", "1"}, {"letter.c.c", "1"}, {"letter.b.b", "1"}, 
   {"letter.a.a", "1"}, {"letter.e.e", "1"}, {"letter.d.d", "1"}};

So, we just need to sort l1 using SortBy as follows:

l3 = SortBy[l1, StringPart[StringReverse[#], 1] &];

And finally, proceed as we did at the beginning:

(s |-> {First@s[[1]], Last@s[[-1]]}) /@ Thread[{l3, SortBy[l2, First]}]

(*{{"letter.a.a", 5}, {"letter.b.b", 7}, {"letter.c.c", 9},
   {"letter.d.d", 6}, {"letter.e.e", 8}, {"letter.f.f", 10}}*)
$\endgroup$
2
$\begingroup$

Using Lookup:

Clear["Global`*"];
subs[products_List, price_List] := Module[{lut = Rule @@@ price},
  {First@#
     , Lookup[lut
      , Last@StringSplit[First@#, "."]
      , "Not found"]
     } & /@ products
  ]

l1 = {{"letter.a.a", "1"}, {"letter.b.b", "1"}, {"letter.c.c", 
    "1"}, {"letter.d.d", "1"}, {"letter.e.e", "1"}, {"letter.f.f", 
    "1"}};
l2 = {{"f", 10}, {"c", 9}, {"e", 8}, {"b", 7}, {"d", 6}, {"a", 5}};

subs[l1, l2]

{{"letter.a.a", 5}, {"letter.b.b", 7}, {"letter.c.c", 
  9}, {"letter.d.d", 6}, {"letter.e.e", 8}, {"letter.f.f", 10}}

l1 = {{"Food.ALMOND.ALMONDCINNAMON", 
    "1"}, {"Food.BLACK_CURRANT.BLACK_CURRANTCORN", 
    "1"}, {"Food.BELL_PEPPER.BELL_PEPPERLIME", "1"}};
l2 = {{"BLACK_CURRANTCORN", 4}, {"BELL_PEPPERLIME", 
    3}, {"ALMONDCINNAMON", 2}};

subs[l1, l2]

{{"Food.ALMOND.ALMONDCINNAMON", 2}, {"Food.BLACK_CURRANT.BLACK_CURRANTCORN", 4}, {"Food.BELL_PEPPER.BELL_PEPPERLIME", 3}}

$\endgroup$
2
$\begingroup$
a =
  {{"letter.a.a", "1"}, {"letter.b.b", "1"}, {"letter.c.c", "1"}, 
   {"letter.d.d", "1"}, {"letter.e.e", "1"}, {"letter.f.f", "1"}};

b = {{"f", 10}, {"c", 9}, {"e", 8}, {"b", 7}, {"d", 6}, {"a", 5}};

Using Merge

With[{c = First /@ a},
 Values @ Merge[{Thread[StringTake[c, -1] -> c], Rule @@@ b}, Identity]]

{{"letter.a.a", 5}, {"letter.b.b", 7}, {"letter.c.c", 9},
{"letter.d.d", 6}, {"letter.e.e", 8}, {"letter.f.f", 10}}

$\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.