25
$\begingroup$

Consider an example Association:

assoc = <|"a" -> 1, "b" -> "x", "this_key_is_too_long_to_type" -> {1}|>;

Suppose I want to replace "this_key_is_too_long_to_type" with "c". I can replace it by transforming into Normal land and back into Association land:

Association[
  ReplaceAll[Normal[assoc], Rule["this_key_is_too_long_to_type", rhs_] :> Rule["c", rhs]]]

But I have found with Associations that there is usually a compact and efficient way and that this double-transformation is usually a signal that I'm not using it. What's the best way to rename a key?

$\endgroup$
  • 1
    $\begingroup$ Based on the complexity of the answers, I'm going to wager that "... there is usually a compact and efficient way..." is false in this case. $\endgroup$ – bobthechemist Nov 3 '14 at 22:42
  • 2
    $\begingroup$ @bobthechemist, if that's the case, let's use this as an opportunity to request ReplaceKey or something in a future version. $\endgroup$ – ArgentoSapiens Nov 4 '14 at 0:50
  • $\begingroup$ ...or at least a more general KeyMapAt or AssociationMapAt $\endgroup$ – Rojo Nov 5 '14 at 11:38
  • $\begingroup$ What property does an Association have that implies that ReplaceAll cannot replace keys? What is the general rule at play here? $\endgroup$ – Alan Jul 14 '17 at 21:35
15
$\begingroup$

There is, of course:

assoc = <|"a" -> 1, "b" -> "x", "this_key_is_too_long_to_type" -> {1}|>;
assoc["c"] = assoc["this_key_is_too_long_to_type"];
assoc["this_key_is_too_long_to_type"] =.
assoc
(* <|"a" -> 1, "b" -> "x", "c" -> {1}|> *)

Not sure if there's an elegant way to do it in one step.

$\endgroup$
13
$\begingroup$

Somewhat similar to the answer of evanb, but without explicit mutations:

keyRename[a_, old_ -> new_] /; KeyExistsQ[a, old] := KeyDrop[old]@Append[a, new -> a[old]]

So that

keyRename[assoc, "this_key_is_too_long_to_type" -> "c"]

(* <|"a" -> 1, "b" -> "x", "c" -> {1}|> *)

It should be noted that this solution doesn't preserve the order of the keys, so if that matters, then solution of evanb is better.

$\endgroup$
  • $\begingroup$ Hi, Leonid. Your method seems only works for one key. What if I have a bunch to replace. I can't understand why association can not use simply /. For example, assoc/.{c1->d1,c2->d2,...} to replace many keys in a single step. I encountered this kind of occasion many times, quite annoying. $\endgroup$ – matheorem Nov 2 '15 at 1:17
  • $\begingroup$ @matheorem I think that massive key renaming is not such a common (frequently needed) operation. Using ReplaceAll will be ambiguous - do you want to replace keys or values. Since KeyDrop and Append are constant-time for associations, you can in principle use my keyRename with Fold: Fold[keyRename, assoc, replacements]. Or, if speed is important, do something more clever, all at once, that would be a few times faster. $\endgroup$ – Leonid Shifrin Nov 2 '15 at 13:45
  • $\begingroup$ @LeonidShifrin, even shorter, along the lines of ArgentoSapiens's O(n): keyReplace[rule_] := KeyMap[Replace[rule]]; keyReplace[{rules__}] := KeyMap[Replace[{rules}]]; /// you'd be surprised how often it's necessary to replace keys programmatically - namespace normalization from multiple sources. $\endgroup$ – alancalvitti May 7 '17 at 17:20
7
$\begingroup$

Here's a way to do it in one line without using Normal first.

KeyMap[If[SameQ[#, "this_key_is_too_long_to_type"], "c", #] &, assoc]

Or:

KeyMap[# /. "this_key_is_too_long_to_type" -> "c" &, assoc]
$\endgroup$
  • 2
    $\begingroup$ The problem with this code is that it is O(n), where n is the number of keys, while it should better be O(1). $\endgroup$ – Leonid Shifrin Nov 3 '14 at 22:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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