How to track part of symbol or how to symbolize parts of symbol without copying data?

Summing up:

  • There is no built-in way to track part of a symbol.

  • Albert's answer uses additional symbol for each part we track. It is useful to digest that code as this idiom can help in variety of cases (e.g. listening to Length[a], etc). Unfortunately it does not scale when you want to track hundreds of indices or you don't know how many you will need.

  • Alternative but limited solution is proposed in Scaling Dynamic widgets up. It constructs a DynamicModule with autogenerated symbols based on desired expression structure. That solution was not flexible enough for my daily use cases

Finally, I think I have something that fits my needs, it is presented in another answer in:

Scaling Dynamic widgets up and https://github.com/kubaPod/PreciseTracking

It is designed to work with associations because they are handy anyway and using lists encourages working with ranges of indices / spans etc and that will be tough to handle.

To not repeat linked examples I will briefly explain here how Dynamics work, how that solution works and how we could adapt it for List/Part if needed.

How Dynamic works

Actors: FE-FrontEnd, K-Kernel:

  • FE->K : user input "Dynamic[x]"
  • K : "Dynamic[x]" -> Dynamic[Global`x] -> DynamicBox[ToBoxes[x, StandardForm]]
  • K->FE : here is the result, a DynamicBox
  • FE : nice, let me create a 'node' in an object that represents the notebook state
  • FE->K : could you please run ToBoxes[x, StandardForm] this for me? I need to display something. Also, please 'track' that expression and let me know if it changes, here is an ID of a 'node' which cares about it.
  • K : ok, it seems that next time x changes then I need to notify FE with ID.
  • K->FE : ToBoxes[x, StandardForm] -> "1"

[coffee break]

  • user : x=2 (I am skipping in/out communication for that call)
  • K : ok, x mutated! let me inform the FE and I can finally remove it from tracking list.
  • K->FE : you better update ID!
  • FE : is ID visible at all? If not I will add it to a queue for later. If it is:
  • FE->K : ok, gimme ToBoxes[x, StandardForm] and please track it again.

[the story repeats]

To take control over Dynamic we have to:

  1. Suppress default tracking (TrackedSymbols :> {})
  2. Capture box ID (ValueTrackGetTrackingState[]`)
  3. Establish 'tracking' for desired expressions
  4. Handle changes of tracked expressions
  5. Tell FE to update given ID.(FrontEnd`UpdateDynamicObjects)
  6. Keep the loop alive if needed

Points 1, 2 and 5 are easy (if you know them). Unfortunately SetValueTrack and friends are internal functions optimized for symbols tracking so we can't use them and don't really want to. The idea is to handle tracking manually:

  1. Dynamic[Track[asso[key]]; whatever, TrackedSymbols :> {}]

  2. V-G trick to call 5. when asso[key] = val is executed

  3. by keeping Track[...] in Dynamic the loop is self sustaining.

Implementation can be found in https://github.com/kubaPod/PreciseTracking/blob/master/PreciseTracking/PreciseTracking.m and is very short. Once it grows I will bring here a small example for clarity.


I understand that you are looking for sort of a conditional updating: only when the displayed portion of the variable test changes. My feeling is that it is unavoidable that the displayed portion of test has to be recomputed at each change of test. But that can be done outside Dynamic. Here is an smaller example, showing the sort of solution I am thinking of:

displaypart[]:=(a=test[[1]];);
test={1,2,3,4,5};
displaypart[];
Dynamic[{RandomReal[], a}]

(* {0.228823,1} *)

I added a RandomReal[] so that it can be seen whether there was an updating or not. Now two experiments:

test[[5]]=12;displaypart[]

Nothing happens with the Dynamic output.

test[[1]]=12;displaypart[]

The dynamic output now shows a different real and the integer 12.

I am not quite sure that this is what you are looking for.


I'm not sure whether the following will fulfill all of your requirements as it does make a copy of the part which is to be shown. But that copy is only used to control when updates are needed and only within a purly local variable. This should not be a problem concerning the updating of the original symbol, but in case you are concerned about the memory consumption of the copy the following will not fulfill the "no copy" requirement (if the part is still a large amount of data one probably could store just a hash value of it instead of the full data and use that for the comparation...). The whole approach is not very elegant but I think should at least come very close to what you want:

SetAttributes[h,HoldFirst];
h[data_Symbol[[partspec___]]]:=DynamicModule[{
        current=data[[partspec]],trigger=0,doupdate=True
    },
    DynamicWrapper[
        Dynamic[
            trigger;
            Column[{
                DateString[],
                InputField[Dynamic[
                    data[[partspec]],
                    (doupdate=False;current=data[[partspec]]=#;FinishDynamic[];doupdate=True)&
                ],Number]
            }],
            TrackedSymbols:>{trigger}
        ],
        If[And[TrueQ[doupdate],current!=data[[partspec]]],trigger=AbsoluteTime[]],
        TrackedSymbols:>{data}
    ]
]


test = RandomReal[1, {2, 2}];
h[test[[1,1]]]

you should now see that the output is updated when you do:

test[[1, 1]] = RandomReal[];

but not when you do:

test[[2, 1]] = RandomReal[];

if you edit the input field, test[[1,1]] will be updated as required.

I think that the same can probably be achieved with only using two additional dynamic variables (or one?) but my own tries to achieve that did all suffer from the problem that the InputField is then recreated when the change is made from within it and loose the focus in the process, which I find quite annoying.