2
$\begingroup$

I want to transform some nested lists into nested associations except the "rows"->List pattern.

Here is the example

s={"sensor" -> {"opto" -> {"filename" -> 
      "137fb39d9a39f0e18ec49f4a68d72554_opto.csv", 
     "data" -> {"time" -> {"unit" -> "s", "rows" -> {1}}, 
       "4" -> {"unit" -> "intensity", "rows" -> {2, 3}}, 
       "1" -> {"unit" -> "intensity", "rows" -> {4, 5}}}}, 
   "qmb1" -> {"filename" -> 
      "137fb39d9a39f0e18ec49f4a68d72554_qmb1.csv", 
     "data" -> {"qmb1" -> {"unit" -> "intensity", "rows" -> {2}}, 
       "time" -> {"unit" -> "s", "rows" -> {1}}}}, 
   "saw" -> {"filename" -> "137fb39d9a39f0e18ec49f4a68d72554_saw.csv",
      "data" -> {"time" -> {"unit" -> "s", "rows" -> {1}}, 
       "15" -> {"unit" -> "Hz", "rows" -> {5, 6}}, 
       "10" -> {"unit" -> "Hz", "rows" -> {2, 3, 4}}, 
       "9" -> {"unit" -> "Hz", "rows" -> {7, 8, 9}}}}, 
   "qmb2" -> {"filename" -> 
      "137fb39d9a39f0e18ec49f4a68d72554_qmb2.csv", 
     "data" -> {"time" -> {"unit" -> "s", "rows" -> {1}}, 
       "qmb2" -> {"unit" -> "intensity", "rows" -> {2}}, 
       "flow" -> {"unit" -> "sscm", "rows" -> {4}}, 
       "temperature" -> {"unit" -> "\[Degree]C", "rows" -> {3}}, 
       "humidity" -> {"unit" -> "%", "rows" -> {5}}}}}, 
 "date" -> {"year" -> 2016, "month" -> 11, "sec" -> 10, "day" -> 17, 
   "hour" -> 14, "min" -> 18}, "serie" -> 1, 
 "compound" -> "multi-detection", "inLab" -> 0, "proto" -> 1, 
 "uuid" -> "137fb39d9a39f0e18ec49f4a68d72554"}

So far I have done:

ReplaceAll[s, List :> Association]

which gives

<|"sensor" -> <|"opto" -> <|"filename" -> 
      "137fb39d9a39f0e18ec49f4a68d72554_opto.csv", 
     "data" -> <|"time" -> <|"unit" -> "s", 
         "rows" -> Association[1]|>, 
       "4" -> <|"unit" -> "intensity", "rows" -> Association[2, 3]|>, 
       "1" -> <|"unit" -> "intensity", 
         "rows" -> Association[4, 5]|>|>|>, 
   "qmb1" -> <|"filename" -> 
      "137fb39d9a39f0e18ec49f4a68d72554_qmb1.csv", 
     "data" -> <|"qmb1" -> <|"unit" -> "intensity", 
         "rows" -> Association[2]|>, 
       "time" -> <|"unit" -> "s", "rows" -> Association[1]|>|>|>, 
   "saw" -> <|"filename" -> 
      "137fb39d9a39f0e18ec49f4a68d72554_saw.csv", 
     "data" -> <|"time" -> <|"unit" -> "s", 
         "rows" -> Association[1]|>, 
       "15" -> <|"unit" -> "Hz", "rows" -> Association[5, 6]|>, 
       "10" -> <|"unit" -> "Hz", "rows" -> Association[2, 3, 4]|>, 
       "9" -> <|"unit" -> "Hz", "rows" -> Association[7, 8, 9]|>|>|>, 
   "qmb2" -> <|"filename" -> 
      "137fb39d9a39f0e18ec49f4a68d72554_qmb2.csv", 
     "data" -> <|"time" -> <|"unit" -> "s", 
         "rows" -> Association[1]|>, 
       "qmb2" -> <|"unit" -> "intensity", "rows" -> Association[2]|>, 
       "flow" -> <|"unit" -> "sscm", "rows" -> Association[4]|>, 
       "temperature" -> <|"unit" -> "\[Degree]C", 
         "rows" -> Association[3]|>, 
       "humidity" -> <|"unit" -> "%", 
         "rows" -> Association[5]|>|>|>|>, 
 "date" -> <|"year" -> 2016, "month" -> 11, "sec" -> 10, "day" -> 17, 
   "hour" -> 14, "min" -> 18|>, "serie" -> 1, 
 "compound" -> "multi-detection", "inLab" -> 0, "proto" -> 1, 
 "uuid" -> "137fb39d9a39f0e18ec49f4a68d72554"|>

However I am stuck here, because ReplaceAll does not seem to work for Association as it works for List. By example:

ReplaceAll[Normal@<|"unit" -> "intensity","rows" -> Association[4, 5]|>, 
           ("rows" -> a_Association) :> ("rows" -> Apply[List, a])]

gives (as expected)

{"unit" -> "intensity", "rows" -> {4, 5}}

but

ReplaceAll[<|"unit" -> "intensity","rows" -> Association[4, 5]|>, 
           ("rows" -> a_Association) :> ("rows" -> Apply[List, a])]

has no effect

<|"unit" -> "intensity", "rows" -> Association[4, 5]|>

Questions

  • why ReplaceAll does not work for Association?
  • how to turns "rows"->Association[4,5,...] into "rows"->{4,5,...} in my association ("rows"->... can be at any place)

Note: I am using MMA v10

$\endgroup$
4
  • $\begingroup$ I think what you are looking for is ToAssociations from the "GeneralUtilities`" package, for which you can find examples in the answers to other questions. Associations were new with 10.0 and replacing within associations was not working from the beginning, so it would be necessary to know which exact version you are using if you are seeking for alternatives to ToAssociations... $\endgroup$ Commented May 11, 2019 at 11:15
  • $\begingroup$ I think this might be a duplicate... $\endgroup$ Commented May 11, 2019 at 11:18
  • $\begingroup$ @AlbertRetey you are right, thanks to pointing this out $\endgroup$ Commented May 11, 2019 at 11:29
  • $\begingroup$ @AlbertRetey the version I am using right now is v10.0, I am also interested by solution working under MMA v11.3 $\endgroup$ Commented May 11, 2019 at 11:33

1 Answer 1

2
$\begingroup$

Note: This answers the question of how to achieve the specific replacement in the question and why the original attempt does not work. To do the actual conversion, it is better to use GeneralUtilities`ToAssociations, as mentioned in the comments, and the question linked there.

The problem is that keys of associations are invisible to most functions, similar to how you don't see indices in normal lists (you can think of the keys as named indices). To see this, consider:

<|"a" -> "a", "b" -> {"a", "b"}|> /. "a" -> "xx"
(* <|"a" -> "xx", "b" -> {"xx", "b"}|> *)

As you can see, the keys are not touched.

There are several ways to work around this:

  • Use MapAt to transform the given entry. This will not work in all cases, but it is very clear in its intent.

    MapAt[Apply[List], "rows"]@<|"unit" -> "intensity", "rows" -> Association[4, 5]|>
    (* <|"unit" -> "intensity", "rows" -> {4, 5}|> *)
    

    You can also use Query to the same effect:

    Query[{"rows" -> Apply[List]}]@<|"unit" -> "intensity", "rows" -> Association[4, 5]|>
    (* <|"unit" -> "intensity", "rows" -> {4, 5}|> *)
    
  • Use the second syntax of AssociationMap. This only works for the top-level association in its current form, but is otherwise very close in functionality:

    AssociationMap[
     Replace[("rows" -> a_Association) :> ("rows" -> Apply[List, a])],
     <|"unit" -> "intensity", "rows" -> Association[4, 5]|>
     ]
    
    <|"unit" -> "intensity", "rows" -> {4, 5}|>
    
  • Match the whole association. This is pretty close to your original attempt, but has the disadvantage that the association can only be matched once, and it a bit harder to read. (this appears to only work starting with 11.0 or so)

    ReplaceAll[
     <|"unit" -> "intensity", "rows" -> Association[4, 5]|>,
     <|pre___, "rows" -> a_Association, post___|> :>
      <|pre, "rows" -> Apply[List, a], post|>
     ]
    (* <|"unit" -> "intensity", "rows" -> Association[4, 5]|> *)
    
$\endgroup$
5
  • $\begingroup$ thanks for the answer. Do you have an idea how to do this without any assumptions? I mean remplacing "rows"->Association[...] by "rows"->List{...} without any further assumptions? $\endgroup$ Commented May 11, 2019 at 11:38
  • $\begingroup$ Which assumptions do you mean exactly? $\endgroup$
    – Lukas Lang
    Commented May 11, 2019 at 11:39
  • $\begingroup$ not using the fact that "rows" key is a member of <|"unit"->... etc... |>. Said another way a solution that works for my whole example, not only for the <|"unit"->... etc... |> part $\endgroup$ Commented May 11, 2019 at 11:40
  • $\begingroup$ As mentioned in the note I've added to the top of the answer, I'd suggest you use GeneralUtilities`ToAssociations for your problem (in all versions of Mathematica). The answer is more intended as a "Why does this not work and how to fix it" than as a "How to do it best" $\endgroup$
    – Lukas Lang
    Commented May 11, 2019 at 11:47
  • $\begingroup$ ops, sorry. I will test that, thanks $\endgroup$ Commented May 11, 2019 at 11:49

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.