private IChangeSet <T> Page(IChangeSet <T> changeset = null) { if (changeset != null) { _all.Clone(changeset); } var previous = _paged; int pages = CalculatePages(); int page = _parameters.Page > pages ? pages : _parameters.Page; int skip = _parameters.Size * (page - 1); var current = _all.Skip(skip) .Take(_parameters.Size) .ToList(); var adds = current.Except(previous); var removes = previous.Except(current); _paged.RemoveMany(removes); adds.ForEach(t => { var index = current.IndexOf(t); _paged.Insert(index, t); }); var startIndex = skip; var moves = changeset.EmptyIfNull() .Where(change => change.Reason == ListChangeReason.Moved && change.MovedWithinRange(startIndex, startIndex + _parameters.Size)); foreach (var change in moves) { //check whether an item has moved within the same page var currentIndex = change.Item.CurrentIndex - startIndex; var previousIndex = change.Item.PreviousIndex - startIndex; _paged.Move(previousIndex, currentIndex); } //find replaces [Is this ever the case that it can be reached] for (int i = 0; i < current.Count; i++) { var currentItem = current[i]; var previousItem = previous[i]; if (ReferenceEquals(currentItem, previousItem)) { continue; } var index = _paged.IndexOf(currentItem); _paged.Move(i, index); } return(_paged.CaptureChanges()); }
private static IChangeSet <T> Virtualise(List <T> all, ChangeAwareList <T> virtualised, IVirtualRequest request, IChangeSet <T> changeset = null) { if (changeset != null) { all.Clone(changeset); } var previous = virtualised; var current = all.Skip(request.StartIndex) .Take(request.Size) .ToList(); var adds = current.Except(previous); var removes = previous.Except(current); virtualised.RemoveMany(removes); adds.ForEach(t => { var index = current.IndexOf(t); virtualised.Insert(index, t); }); var moves = changeset.EmptyIfNull() .Where(change => change.Reason == ListChangeReason.Moved && change.MovedWithinRange(request.StartIndex, request.StartIndex + request.Size)); foreach (var change in moves) { //check whether an item has moved within the same page var currentIndex = change.Item.CurrentIndex - request.StartIndex; var previousIndex = change.Item.PreviousIndex - request.StartIndex; virtualised.Move(previousIndex, currentIndex); } //find replaces [Is this ever the case that it can be reached] for (var i = 0; i < current.Count; i++) { var currentItem = current[i]; var previousItem = previous[i]; if (ReferenceEquals(currentItem, previousItem)) { continue; } var index = virtualised.IndexOf(currentItem); virtualised.Move(i, index); } return(virtualised.CaptureChanges()); }
public IList <Change <TObject, TKey> > Calculate(IKeyValueCollection <TObject, TKey> currentItems, IKeyValueCollection <TObject, TKey> previousItems, IChangeSet <TObject, TKey> sourceUpdates) { if (currentItems.SortReason == SortReason.ComparerChanged) { //clear collection and rebuild var removed = previousItems.Select((item, index) => new Change <TObject, TKey>(ChangeReason.Remove, item.Key, item.Value, index)); var newitems = currentItems.Select((item, index) => new Change <TObject, TKey>(ChangeReason.Add, item.Key, item.Value, index)); return(new List <Change <TObject, TKey> >(removed.Union(newitems))); } var previousList = previousItems.ToList(); var keyComparer = new KeyComparer <TObject, TKey>(); var removes = previousItems.Except(currentItems, keyComparer).ToList(); var adds = currentItems.Except(previousItems, keyComparer).ToList(); var inbothKeys = previousItems.Intersect(currentItems, keyComparer) .Select(x => x.Key).ToHashSet(); var result = new List <Change <TObject, TKey> >(); foreach (var remove in removes) { int index = previousList.IndexOf(remove); previousList.RemoveAt(index); result.Add(new Change <TObject, TKey>(ChangeReason.Remove, remove.Key, remove.Value, index)); } foreach (var add in adds) { //find new insert position int index = previousList.BinarySearch(add, currentItems.Comparer); int insertIndex = ~index; previousList.Insert(insertIndex, add); result.Add(new Change <TObject, TKey>(ChangeReason.Add, add.Key, add.Value, insertIndex)); } //Adds and removes ahave been accounted for //so check whether anything in the remaining change set have been moved ot updated var remainingItems = sourceUpdates .EmptyIfNull() .Where(u => inbothKeys.Contains(u.Key) && (u.Reason == ChangeReason.Update || u.Reason == ChangeReason.Moved || u.Reason == ChangeReason.Evaluate)) .ToList(); foreach (var change in remainingItems) { if (change.Reason == ChangeReason.Update) { var current = new KeyValuePair <TKey, TObject>(change.Key, change.Current); var previous = new KeyValuePair <TKey, TObject>(change.Key, change.Previous.Value); //remove from the actual index var removeIndex = previousList.IndexOf(previous); previousList.RemoveAt(removeIndex); //insert into the desired index int desiredIndex = previousList.BinarySearch(current, currentItems.Comparer); int insertIndex = ~desiredIndex; previousList.Insert(insertIndex, current); result.Add(new Change <TObject, TKey>(ChangeReason.Update, current.Key, current.Value, previous.Value, insertIndex, removeIndex)); } else if (change.Reason == ChangeReason.Moved) { //TODO: We have the index already, would be more efficient to calculate new position from the original index var current = new KeyValuePair <TKey, TObject>(change.Key, change.Current); var previousindex = previousList.IndexOf(current); int desiredIndex = previousList.BinarySearch(current, currentItems.Comparer); int insertIndex = ~desiredIndex; //this should never be the case, but check anyway if (previousindex == insertIndex) { continue; } if (insertIndex < 0) { throw new SortException("Cannot determine current index"); } previousList.RemoveAt(previousindex); previousList.Insert(insertIndex, current); result.Add(new Change <TObject, TKey>(current.Key, current.Value, insertIndex, previousindex)); } else { //TODO: re-evaluate to check whether item should be moved result.Add(change); } } //Alternative to evaluate is to check order var evaluates = remainingItems.Where(c => c.Reason == ChangeReason.Evaluate) .OrderByDescending(x => new KeyValuePair <TKey, TObject>(x.Key, x.Current), currentItems.Comparer) .ToList(); //calculate moves. Very expensive operation //TODO: Try and make this better foreach (var u in evaluates) { var current = new KeyValuePair <TKey, TObject>(u.Key, u.Current); var old = previousList.IndexOf(current); if (old == -1) { continue; } int newposition = GetInsertPositionLinear(previousList, current, currentItems.Comparer); if (old < newposition) { newposition--; } if (old == newposition) { continue; } previousList.RemoveAt(old); previousList.Insert(newposition, current); result.Add(new Change <TObject, TKey>(u.Key, u.Current, newposition, old)); } return(result); }
private static PageChangeSet <T> Page(List <T> all, ChangeAwareList <T> paged, IPageRequest request, IChangeSet <T>?changeSet = null) { if (changeSet is not null) { all.Clone(changeSet); } var previous = paged; int pages = CalculatePages(all, request); int page = request.Page > pages ? pages : request.Page; int skip = request.Size * (page - 1); var current = all.Skip(skip) .Take(request.Size) .ToList(); var adds = current.Except(previous); var removes = previous.Except(current); paged.RemoveMany(removes); adds.ForEach(t => { var index = current.IndexOf(t); paged.Insert(index, t); }); var startIndex = skip; var moves = changeSet.EmptyIfNull() .Where(change => change.Reason == ListChangeReason.Moved && change.MovedWithinRange(startIndex, startIndex + request.Size)); foreach (var change in moves) { // check whether an item has moved within the same page var currentIndex = change.Item.CurrentIndex - startIndex; var previousIndex = change.Item.PreviousIndex - startIndex; paged.Move(previousIndex, currentIndex); } // find replaces [Is this ever the case that it can be reached] for (int i = 0; i < current.Count; i++) { var currentItem = current[i]; var previousItem = previous[i]; if (ReferenceEquals(currentItem, previousItem)) { continue; } var index = paged.IndexOf(currentItem); paged.Move(i, index); } var changed = paged.CaptureChanges(); return(new PageChangeSet <T>(changed, new PageResponse(paged.Count, page, all.Count, pages))); }