# Manipulation of two lists of strings and integers

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.

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}}

• 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. Commented Jan 15, 2019 at 16:58
• I just updated the question, please see. Commented Jan 15, 2019 at 17:04
• @William, updated.
– kglr
Commented Jan 15, 2019 at 17:17

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.

Just for fun also :)

Map[{#[[1, 1]], #[[2, 2]]} &, GatherBy[Flatten[{l1, l2}, 1], (Last@StringSplit[#[[1]], "."]) &]]

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}}

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}}*)


Using Lookup:

Clear["Global*"];
subs[products_List, price_List] := Module[{lut = Rule @@@ price},
{First@#
, Lookup[lut
, Last@StringSplit[First@#, "."]
} & /@ 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}}

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}}