Consider two lists of rules:

list1 = {"a" -> "apple", "b" -> "banana", "c" -> "cucumber"};
list2 = {"A" -> "pie", "B" -> "split", "C" -> "salad"};

I'm looking to come up with a way to "combine" these lists into a third list of rules Rule[key, val] in which key is a value from a rule taken from list1 and val is the value taken from the matching rule in list2. "Matching" here is subject to a custom criterion.

In this example, I want to get the list

{"apple" -> "pie", "banana" -> "split", "cucumber" -> "salad"}

where the combining criterion is that the keys consist of the same letter; that is, "a" and "A" satisfy some version of StringMatchQ[#1, #2, IgnoreCase -> True] &, for instance.

Is there a good way to do this?

For this particular example, this works (though I bet there's a neater way to do it):

    {list1 = {"a" -> "apple", "b" -> "banana", "c" -> "cucumber"},
     list2 = {"A" -> "pie", "B" -> "split", "C" -> "salad"}},
    (Reverse /@ list1) /. (list2 /. s_String :> ToLowerCase[s])
(* {"apple" -> "pie", "banana" -> "split", "cucumber" -> "salad"} *)

But in my more general use case, I'll have list1 and list2 with keys like {"x1", "y1", "z1"} and {"X-one", "Y-one", "Z-one"}, with the matching criterion that values to-be-Rule-ified are the ones whose corresponding keys contain 1 and one.

P.S. Sorry if the title is confusing, I wasn't quite sure how to word it.


1 Answer 1

list1 /. Rule[a_, b_] :> Rule[b, ToUpperCase[a] /. list2]

{"apple" -> "pie", "banana" -> "split", "cucumber" -> "salad"}


list2 /. Rule[a_, b_] :> Rule[ToLowerCase[a] /. list1, b]

{"apple" -> "pie", "banana" -> "split", "cucumber" -> "salad"}


Values @ GroupBy[Join[list1, list2], ToLowerCase[#[[1]]] &,  Apply[Rule]@*Values]

{"apple" -> "pie", "banana" -> "split", "cucumber" -> "salad"}


Values @ Merge[Apply[Rule]][MapAt[ToLowerCase, #, {All, 1}] & /@ {list1, list2}]

{"apple" -> "pie", "banana" -> "split", "cucumber" -> "salad"}

For general cases like the second example in OP, you can define case-specific conversion rules and convert keys of one of the input lists before using Merge or GroupBy:

k2Tok1 = StringReplace[a__ ~~ "-" ~~ b__ :> 
  ToLowerCase[a] <> ToString[Interpreter["SemanticNumber"][b]]] ;

k1Tok2 = StringReplace[ a__ ~~ i : NumberString :>
   ToUpperCase[a] <> "-" <> IntegerName[FromDigits@i]] ;


k1 = {"x1", "y1", "z5"};
k2 = {"X-one", "Y-one", "Z-six"};

k1Tok2 @ k1

{"X-one", "Y-one", "Z-five"}

k2Tok1 @ k2

{"x1", "y1", "z6"}

lst1 = {"x1" -> "apple", "y1" -> "banana", "z6" -> "cucumber"};
lst2 = {"X-one" -> "pie", "Y-one" -> "split", "Z-six" -> "salad"};

lst1 /. Rule[a_, b_] :> Rule[b, k1Tok2[a] /. lst2]

{"apple" -> "pie", "banana" -> "split", "cucumber" -> "salad"}

lst2 /. Rule[a_, b_] :> Rule[k2Tok1[a] /. lst1, b]

{"apple" -> "pie", "banana" -> "split", "cucumber" -> "salad"}


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.