private IChangeSet <T> Reset(List <T> original, ChangeAwareList <T> target) { var sorted = original.OrderBy(t => t, _comparer).ToList(); target.Clear(); target.AddRange(sorted); return(target.CaptureChanges()); }
private IChangeSet <T> ChangeComparer(ChangeAwareList <T> target, IComparer <T> comparer) { _comparer = comparer; var sorted = target.OrderBy(t => t, _comparer).ToList(); target.Clear(); target.AddRange(sorted); return(target.CaptureChanges()); }
private void Requery(Func <T, bool> predicate, List <ItemWithMatch> all, ChangeAwareList <ItemWithMatch> filtered) { if (_policy == ListFilterPolicy.ClearAndReplace) { var newMatches = all.Where(iwm => predicate(iwm.Item)).ToList(); //mark items as matched? filtered.Clear(); filtered.AddRange(newMatches); //reset state all.Where(iwm => iwm.IsMatch).ForEach(iwm => iwm.IsMatch = false); newMatches.ForEach(iwm => iwm.IsMatch = true); return; } var mutatedMatches = new List <Action>(all.Count); var newState = all.Select(item => { var match = predicate(item.Item); var wasMatch = item.IsMatch; //Mutate match - defer until filtered list has been modified //[to prevent potential IndexOf failures] if (item.IsMatch != match) { mutatedMatches.Add(() => item.IsMatch = match); } return(new { Item = item, IsMatch = match, WasMatch = wasMatch }); }).ToList(); //reflect items which are no longer matched var noLongerMatched = newState.Where(state => !state.IsMatch && state.WasMatch).Select(state => state.Item); filtered.RemoveMany(noLongerMatched); //reflect new matches in the list var newMatched = newState.Where(state => state.IsMatch && !state.WasMatch).Select(state => state.Item); filtered.AddRange(newMatched); //finally apply mutations mutatedMatches.ForEach(m => m()); }
private IChangeSet <T> ChangeComparer(ChangeAwareList <T> target, IComparer <T> comparer) { _comparer = comparer; if (_resetThreshold > 0 && target.Count <= _resetThreshold) { return(Reorder(target)); } var sorted = target.OrderBy(t => t, _comparer).ToList(); target.Clear(); target.AddRange(sorted); return(target.CaptureChanges()); }
private IChangeSet <ItemWithMatch> Requery(Func <T, bool> predicate, List <ItemWithMatch> all, ChangeAwareList <ItemWithMatch> filtered) { if (all.Count == 0) { return(ChangeSet <ItemWithMatch> .Empty); } if (_policy == ListFilterPolicy.ClearAndReplace) { var itemsWithMatch = all.Select(iwm => new ItemWithMatch(iwm.Item, predicate(iwm.Item), iwm.IsMatch)).ToList(); //mark items as matched? filtered.Clear(); filtered.AddRange(itemsWithMatch.Where(iwm => iwm.IsMatch)); //reset state for all items all.Clear(); all.AddRange(itemsWithMatch); return(filtered.CaptureChanges()); } var toAdd = new List <ItemWithMatch>(all.Count); var toRemove = new List <ItemWithMatch>(all.Count); for (int i = 0; i < all.Count; i++) { var original = all[i]; var newItem = new ItemWithMatch(original.Item, predicate(original.Item), original.IsMatch); all[i] = newItem; if (newItem.IsMatch && !newItem.WasMatch) { toAdd.Add(newItem); } else if (!newItem.IsMatch && newItem.WasMatch) { toRemove.Add(newItem); } } filtered.RemoveMany(toRemove); filtered.AddRange(toAdd); return(filtered.CaptureChanges()); }
private static IChangeSet <TValue> Process(Dictionary <TValue, int> values, ChangeAwareList <TValue> result, IChangeSet <ItemWithMatch> changes) { void AddAction(TValue value) => values.Lookup(value) .IfHasValue(count => values[value] = count + 1) .Else(() => { values[value] = 1; result.Add(value); }); void RemoveAction(TValue value) { var counter = values.Lookup(value); if (!counter.HasValue) { return; } //decrement counter var newCount = counter.Value - 1; values[value] = newCount; if (newCount != 0) { return; } //if there are none, then remove and notify result.Remove(value); values.Remove(value); } foreach (var change in changes) { switch (change.Reason) { case ListChangeReason.Add: { var value = change.Item.Current.Value; AddAction(value); break; } case ListChangeReason.AddRange: { change.Range.Select(item => item.Value).ForEach(AddAction); break; } case ListChangeReason.Refresh: { var value = change.Item.Current.Value; var previous = change.Item.Current.Previous; if (value.Equals(previous)) { continue; } RemoveAction(previous); AddAction(value); break; } case ListChangeReason.Replace: { var value = change.Item.Current.Value; var previous = change.Item.Previous.Value.Value; if (value.Equals(previous)) { continue; } RemoveAction(previous); AddAction(value); break; } case ListChangeReason.Remove: { var previous = change.Item.Current.Value; RemoveAction(previous); break; } case ListChangeReason.RemoveRange: { change.Range.Select(item => item.Value).ForEach(RemoveAction); break; } case ListChangeReason.Clear: { result.Clear(); values.Clear(); break; } } } return(result.CaptureChanges()); }
private IChangeSet <T> ProcessImpl(ChangeAwareList <T> target, IChangeSet <T> changes) { var refreshes = new List <T>(changes.Refreshes); foreach (var change in changes) { switch (change.Reason) { case ListChangeReason.Add: { var current = change.Item.Current; Insert(target, current); break; } case ListChangeReason.AddRange: { var ordered = change.Range.OrderBy(t => t, _comparer).ToList(); if (target.Count == 0) { target.AddRange(ordered); } else { ordered.ForEach(item => Insert(target, item)); } break; } case ListChangeReason.Remove: { var current = change.Item.Current; Remove(target, current); break; } case ListChangeReason.Refresh: { // add to refresh list so position can be calculated refreshes.Add(change.Item.Current); // add to current list so downstream operators can receive a refresh // notification, so get the latest index and pass the index up the chain var indexed = target.IndexOfOptional(change.Item.Current).ValueOrThrow(() => new SortException($"Cannot find index of {typeof(T).Name} -> {change.Item.Current}. Expected to be in the list")); target.Refresh(indexed.Item, indexed.Index); break; } case ListChangeReason.Replace: { var current = change.Item.Current; // TODO: check whether an item should stay in the same position // i.e. update and move Remove(target, change.Item.Previous.Value); Insert(target, current); break; } case ListChangeReason.RemoveRange: { target.RemoveMany(change.Range); break; } case ListChangeReason.Clear: { target.Clear(); break; } } } // Now deal with refreshes [can be expensive] foreach (var item in refreshes) { var old = target.IndexOf(item); if (old == -1) { continue; } int newPosition = GetInsertPositionLinear(target, item); if (old < newPosition) { newPosition--; } if (old == newPosition) { continue; } target.Move(old, newPosition); } return(target.CaptureChanges()); }
private IChangeSet <T> ProcessImpl(ChangeAwareList <T> target, IChangeSet <T> changes) { if (_comparer == null) { target.Clone(changes); return(target.CaptureChanges()); } foreach (var change in changes) { switch (change.Reason) { case ListChangeReason.Add: { var current = change.Item.Current; Insert(target, current); break; } case ListChangeReason.AddRange: { var ordered = change.Range.OrderBy(t => t, _comparer).ToList(); if (target.Count == 0) { target.AddRange(ordered); } else { ordered.ForEach(item => Insert(target, item)); } break; } case ListChangeReason.Replace: { var current = change.Item.Current; //TODO: check whether an item should stay in the same position //i.e. update and move Remove(target, change.Item.Previous.Value); Insert(target, current); break; } case ListChangeReason.Remove: { var current = change.Item.Current; Remove(target, current); break; } case ListChangeReason.RemoveRange: { target.RemoveMany(change.Range); break; } case ListChangeReason.Clear: { target.Clear(); break; } } } return(target.CaptureChanges()); }