phosphorjs/phosphor-observablelist

mirroring function

Closed this issue · 2 comments

It would be really convenient to have a function like my (very, very rough) follow function, which basically just lets one observable list mirror another: https://github.com/jasongrout/jupyter-js-notebook/blob/4cf0e81d019f13bb8992c68b57cc25f4fb5f27bf/src/NotebookWidget.ts#L85
this last thing lets you very easily sync up two lists, so for example, the list of cells automatically maintains the list of widgets with just this: https://github.com/jasongrout/jupyter-js-notebook/blob/4cf0e81d019f13bb8992c68b57cc25f4fb5f27bf/src/NotebookWidget.ts#L45

For completeness, here's my messy, unfinished, rough code:

/* factory is called to create a new sink element from a new source element */
function follow<T,U>(source: IObservableList<T>, 
                                   sink: IObservableList<U>, 
                                   factory: (arg: T)=> U) {  
  // Initialize sink list
  sink.clear();
  for (let i=0; i<source.length; i++) {
    sink.add(factory(source.get(i)))
  }

  source.changed.connect((sender, args) => {
    switch(args.type) {
    case ListChangeType.Add:
      sink.insert(args.newIndex, factory(args.newValue as T))
      break;
    case ListChangeType.Move:
      sink.move(args.oldIndex, args.newIndex);
      break;
    case ListChangeType.Remove:
      sink.removeAt(args.oldIndex);
      break;
    case ListChangeType.Replace:
      sink.replace(args.oldIndex, (args.oldValue as T[]).length, 
                   (args.newValue as T[]).map(factory));
      break;
    case ListChangeType.Set:
      sink.set(args.newIndex, factory(args.newValue as T))
      break;
    }
  });

The use cases for this are going to vary widely, so its unlikely we could make an implementation that satisfies everyone. Closing for now. Will reconsider if it becomes a recurring theme.

Okay. I've already used it twice.