private int GetInsertPositionLinear(IList <KeyValuePair <TKey, TObject> > list, KeyValuePair <TKey, TObject> item) { for (var i = 0; i < list.Count; i++) { if (_comparer.Compare(item, list[i]) < 0) { return(i); } } return(_list.Count); }
/// <summary> /// Dynamic calculation of moved items which produce a result which can be enumerated through in order /// </summary> /// <returns></returns> public IChangeSet <TObject, TKey> Calculate(IChangeSet <TObject, TKey> changes) { var reducedChanges = ReduceChanges(changes).ToList(); var changesByReason = reducedChanges.GroupBy(c => c.Reason).ToDictionary(x => x.Key, x => x.AsEnumerable()); var result = new List <Change <TObject, TKey> >(reducedChanges.Count); var refreshes = changesByReason.GetOrEmpty(ChangeReason.Refresh); var removals = changesByReason.GetOrEmpty(ChangeReason.Remove); var updateChanges = changesByReason.GetOrEmpty(ChangeReason.Update).Concat(refreshes).OrderBy(r => new KeyValuePair <TKey, TObject>(r.Key, r.Current), _comparer); var keysToBeRemoved = updateChanges.Concat(removals).ToDictionary(x => x.Key, x => x); var updatePreviousValues = new Dictionary <KeyValuePair <TKey, TObject>, IndexAndNode <KeyValuePair <TKey, TObject> > >(); if (keysToBeRemoved.Any()) { var index = 0; var node = _list.First; while (node != null) { if (keysToBeRemoved.ContainsKey(node.Value.Key)) { var toBeRemoved = keysToBeRemoved[node.Value.Key]; var nodeCopy = node; if (toBeRemoved.Reason == ChangeReason.Remove) { result.Add(new Change <TObject, TKey>(ChangeReason.Remove, toBeRemoved.Key, toBeRemoved.Current, index)); } else { var kvp = new KeyValuePair <TKey, TObject>(node.Value.Key, toBeRemoved.Current); updatePreviousValues[kvp] = IndexAndNode.Create(index, node); index++; } node = node.Next; _list.Remove(nodeCopy); keysToBeRemoved.Remove(nodeCopy.Value.Key); if (!keysToBeRemoved.Any()) { break; } } else { node = node.Next; index++; } } } var updates = new Queue <Change <TObject, TKey> >(updateChanges); if (updates.Any()) { var index = -1; var node = _list.First; var alreadyAddedNodes = new SortedSet <KeyValuePair <TKey, TObject> >(_comparer); var nodeToBeUpdated = updates.Peek(); var kvp = new KeyValuePair <TKey, TObject>(nodeToBeUpdated.Key, nodeToBeUpdated.Current); while (node != null) { index++; var shouldInsertElement = _comparer.Compare(node.Value, kvp) >= 0; if (shouldInsertElement) { var previous = updatePreviousValues[kvp]; // Previous node index is shifter by count of already added nodes // which are less than the one we are about to insert var indexOffset = alreadyAddedNodes.TakeWhile(kv => _comparer.Compare(kvp, node.Value) < 0).Count(); var previousIndex = previous.Index + indexOffset; var previousValue = previous.Node.Value; var nodeToAdd = new LinkedListNode <KeyValuePair <TKey, TObject> >(kvp); _list.AddBefore(node, nodeToAdd); if (previousIndex == index) { result.Add(new Change <TObject, TKey>(nodeToBeUpdated.Reason, kvp.Key, kvp.Value, previousValue.Value, index, previousIndex)); } else { alreadyAddedNodes.Add(kvp); result.Add(new Change <TObject, TKey>(kvp.Key, kvp.Value, index, previousIndex)); result.Add(new Change <TObject, TKey>(nodeToBeUpdated.Reason, kvp.Key, kvp.Value, previousValue.Value, index, index)); } node = nodeToAdd; updates.Dequeue(); if (!updates.Any()) { break; } nodeToBeUpdated = updates.Peek(); kvp = new KeyValuePair <TKey, TObject>(nodeToBeUpdated.Key, nodeToBeUpdated.Current); } node = node.Next; } if (updates.Any()) { var previous = updatePreviousValues[kvp]; var previousIndex = previous.Index; var previousValue = previous.Node.Value; if (index == -1) { result.Add(new Change <TObject, TKey>(nodeToBeUpdated.Reason, kvp.Key, kvp.Value, previousValue.Value, 0, 0)); } else { result.Add(new Change <TObject, TKey>(kvp.Key, kvp.Value, index, previousIndex)); result.Add(new Change <TObject, TKey>(nodeToBeUpdated.Reason, kvp.Key, kvp.Value, previousValue.Value, index, index)); } _list.AddLast(kvp); } } var adds = new Queue <Change <TObject, TKey> >(changesByReason.GetOrEmpty(ChangeReason.Add).OrderBy(r => r.CurrentIndex)); if (adds.Any()) { var index = -1; var node = _list.First; var nodeToBeAdded = adds.Peek(); var kvp = new KeyValuePair <TKey, TObject>(nodeToBeAdded.Key, nodeToBeAdded.Current); while (node != null) { index++; var shouldInsert = _comparer.Compare(node.Value, kvp) > 0; if (shouldInsert) { var nodeToAdd = new LinkedListNode <KeyValuePair <TKey, TObject> >(kvp); _list.AddBefore(node, nodeToAdd); result.Add(new Change <TObject, TKey>(ChangeReason.Add, nodeToBeAdded.Key, nodeToBeAdded.Current, index)); node = nodeToAdd; adds.Dequeue(); if (!adds.Any()) { break; } nodeToBeAdded = adds.Peek(); kvp = new KeyValuePair <TKey, TObject>(nodeToBeAdded.Key, nodeToBeAdded.Current); } node = node.Next; } if (adds.Any()) { _list.AddLast(kvp); result.Add(new Change <TObject, TKey>(ChangeReason.Add, nodeToBeAdded.Key, nodeToBeAdded.Current, index)); } } return(new ChangeSet <TObject, TKey>(result)); }