6
$\begingroup$

This question already has an answer here:

I expected that something like this

assoc = <|"a" -> "str1", "b" -> <|"a" -> "str2", "b" -> <||>|>|>;
assoc /. <|"a" -> v_, "b" -> <||>|> :> v <> "0"

would evaluate to

<|"a" -> "str1", "b" -> "str20"|>

Obviously, it doesn't.

Instead, the result is

<|"a" -> "str1", "b" -> "str2" <> "0"|>

which can turn into the desired output if it is evaluated eg manually.

I've also tried using KeyValuePattern but the result is the same.

assoc is deceivingly simple in structure because I made it so for clarity of exposition. My actual problem entails deeper associations eg

<|"a" -> "str1", "b" -> <|
  "c1" -> 1, "c2" -> 2, "c3" -> <|
    "a" -> "str2", "b" -> <|
      "c4" -> <|"a" -> "str3", "b" -> <||>|>, "c5" -> 3
     |>
    |>
   |>
  |>

My question is, how to achieve the desired output using rules or if that's not possible what other recourse is there available?

$\endgroup$

marked as duplicate by Leonid Shifrin, m_goldberg, Kuba Sep 29 '18 at 6:17

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 7
    $\begingroup$ Try Replace[<|"a" -> "str1", "b" -> <|"a" -> "str2", "b" -> <||>|>|>, KeyValuePattern[{"a" -> x_, "b" -> <||>}] :> x <> "0", {1}]. $\endgroup$ – J. M. will be back soon Sep 27 '18 at 13:18
  • 1
    $\begingroup$ @J.M. This is certainly worth an answer. You should also mention that the introduction of KeyValuePattern is necessary because Associations are atomic. $\endgroup$ – Henrik Schumacher Sep 27 '18 at 15:02
  • 4
    $\begingroup$ Use RuleCondition: assoc /. <|"a" -> v_, "b" -> <||>|> :> RuleCondition [v <> "0"]. $\endgroup$ – Leonid Shifrin Sep 27 '18 at 23:19
  • $\begingroup$ @LeonidShifrin What is RuleCondition? When I look it up in the help documentation it is not found. $\endgroup$ – Jack LaVigne Sep 28 '18 at 22:15
  • 1
    $\begingroup$ @JackLaVigne In fact, Replace working here looks like a bug. I will report it. $\endgroup$ – Leonid Shifrin Sep 29 '18 at 20:08
7
$\begingroup$

This is more of an extended comment rather than an answer. Using this definition:

assoc = <|"a" -> "str1", "b" -> <|"a" -> "str2", "b" -> <||>|>|>
(* <|"a" -> "str1", "b" -> <|"a" -> "str2", "b" -> <||>|>|> *)

when applying this replacement the result was:

assoc /. <|"a" -> v_, "b" -> <||>|> :> v <> "0"
(* <|"a" -> "str1", "b" -> "str2" <> "0"|> *)

whereas the expected result was:

(* <|"a" -> "str1", "b" -> "str20"|> *)

JM suggested using Replace with KeyValuePattern

Using Replace with KeyValuePattern produces the desired result:

Replace[assoc, KeyValuePattern[{"a" -> v_, "b" -> <||>}] :> v <> "0", {1}]
(* <|"a" -> "str1", "b" -> "str20"|> *)

Result is not related to KeyValuePattern

I discovered that KeyValuePattern is not the cure, rather it was using Replace rather than ReplaceAll.

Observe the results below.

Replace[assoc, <|"a" -> v_, "b" -> <||>|> :> v <> "0", {1}]
(* <|"a" -> "str1", "b" -> "str20"|> *)

ReplaceAll[assoc, <|"a" -> v_, "b" -> <||>|> :> v <> "0"]
(* <|"a" -> "str1", "b" -> "str2" <> "0"|> *)

ReplaceAll[assoc,KeyValuePattern[{"a" -> v_, "b" -> <||>}] :> v <> "0"]
(* <|"a" -> "str1", "b" -> "str2" <> "0"|> *)

Independent of the whether we use KeyValuePattern or not, Replace appears to evaluate the result whereas ReplaceAll returns the result without evaluation.

Using List rather than Association

If rather than using an association we use a list, the problem disappears.

listOfRules = {{"a" -> "str1"}, {"b" -> {"a" -> "str2"}}}
(* {{"a" -> "str1"}, {"b" -> {"a" -> "str2"}}} *)

Replace[listOfRules, {"a" -> v_, "b" -> {}} :> v <> "0", Infinity]
(* {"a" -> "str1", "b" -> "str20"} *)

ReplaceAll[listOfRules, {"a" -> v_, "b" -> {}} :> v <> "0"]
(* {"a" -> "str1", "b" -> "str20"} *)

Particular solution

It is interesting than to see that for this particular problem one could convert the association to a list, make the replacement and then convert it back to an association.

assoc /. Association -> List /. {"a" -> v_, "b" -> {}} :> v <> "0" // Association
(* <|"a" -> "str1", "b" -> "str20"|> *)

I don't recommend this (I think J.M's suggestion is the way to go).

Conclusion

It appears that applying ReplaceAll to an association returns the result without evaluation whereas Relace evaluates the result.

This observation is unrelated to the use of KeyValuePattern.

$\endgroup$
  • $\begingroup$ Looks like I don't need to write an answer anymore... :D $\endgroup$ – J. M. will be back soon Sep 28 '18 at 0:37
  • $\begingroup$ @J.M. Could you explain why Replace evaluates the result while ReplaceAll does not? Is this expected? $\endgroup$ – Alexey Popkov Sep 28 '18 at 1:41
  • $\begingroup$ @Alexey, I actually don't know, which makes Leonid's technique in the comments even more mystifying. $\endgroup$ – J. M. will be back soon Sep 28 '18 at 1:42
4
$\begingroup$

Maybe J.M. doesn't need to write an answer anymore, but I think this answer based on Leonid Shifrin's earlier answer should be recorded.

assoc = <|"a" -> "str1", "b" -> <|"a" -> "str2", "b" -> <||>|>|>;
assoc /. <|"a" -> v_, "b" -> <||>|> :> RuleCondition[v <> "0"]
<|"a" -> "str1", "b" -> "str20"|>
$\endgroup$

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