Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.Linq;
- using System.Windows;
- using System.Linq.Expressions;
- namespace Collections
- {
- internal static class CollectionHelper
- {
- /// <summary>
- /// Combines the left and right into a new list and
- /// makes left and right to be the same as the new list.
- /// </summary>
- /// <param name="left">The left list.</param>
- /// <param name="right">The right list.</param>
- /// <param name="comparer">The item equality comparer.</param>
- public static void Equalize(IList left, IList right, IEqualityComparer comparer)
- {
- if (left.IsReadOnly || right.IsReadOnly)
- {
- return;
- }
- List<object> combined = new List<object>();
- if (left != null && left.Count > 0)
- {
- combined.AddRange(left.OfType<object>());
- }
- if (right != null && right.Count > 0)
- {
- combined.AddRange(right.OfType<object>());
- }
- foreach (var o in combined)
- {
- if (left != null && !left.Any(item => comparer.Equals(item, o)))
- {
- left.Add(o);
- }
- if (right != null && !right.Any(item => comparer.Equals(item, o)))
- {
- right.Add(o);
- }
- }
- }
- /// <summary>
- /// Combines the left and right into a new list and
- /// makes left and right to be the same as the new list.
- /// </summary>
- /// <param name="left">The left list.</param>
- /// <param name="right">The right list.</param>
- public static void Equalize(IList left, IList right)
- {
- Equalize(left, right, EqualityComparer<object>.Default);
- }
- /// <summary>
- /// Makes the target collection a mirror copy of the source, so that they both contain the same items.
- /// </summary>
- /// <param name="target">The target collection.</param>
- /// <param name="source">The source enumerable.</param>
- /// <param name="comparer">The item equality comparer.</param>
- public static void Mirror(IList target, IEnumerable source, IEqualityComparer comparer)
- {
- if (target == null || source == null || target.IsReadOnly)
- {
- return;
- }
- foreach (var item in source)
- {
- if (!target.Any(o => comparer.Equals(item, o)))
- {
- target.Add(item);
- }
- }
- foreach (var item in target.ToObjectList())
- {
- if (!source.Any(o => comparer.Equals(item, o)))
- {
- target.Remove(item);
- }
- }
- }
- /// <summary>
- /// Makes the target collection a mirror copy of the source, so that they both contain the same items.
- /// </summary>
- /// <param name="target">The target collection.</param>
- /// <param name="source">The source enumerable.</param>
- public static void Mirror(IList target, IEnumerable source)
- {
- Mirror(target, source, EqualityComparer<object>.Default);
- }
- /// <summary>
- /// Synchronizes two source and target based on the information
- /// stored in the e parameter.
- /// </summary>
- /// <param name="e">The arguments for synchronization.</param>
- /// <param name="source">The source.</param>
- /// <param name="target">The target.</param>
- public static void Synchronize(NotifyCollectionChangedEventArgs e, IEnumerable source, IList target)
- {
- if (e == null || source == null || target == null)
- {
- return;
- }
- switch (e.Action)
- {
- case NotifyCollectionChangedAction.Add:
- CollectionHelper.Insert(target, e.NewItems, e.NewStartingIndex, EqualityComparer<object>.Default);
- break;
- case NotifyCollectionChangedAction.Remove:
- CollectionHelper.Remove(target, e.OldItems, EqualityComparer<object>.Default);
- break;
- case NotifyCollectionChangedAction.Replace:
- EnsureAllHaveOneElement(e.NewItems, e.OldItems);
- Replace(target, e.NewItems.ElementAt(0), e.OldItems.ElementAt(0), EqualityComparer<object>.Default);
- break;
- case NotifyCollectionChangedAction.Reset:
- Reset(source, target);
- break;
- #if WPF
- case NotifyCollectionChangedAction.Move:
- EnsureAllHaveOneElement(e.NewItems, e.OldItems);
- Move(target, e.NewItems.ElementAt(0), e.NewStartingIndex);
- break;
- #endif
- }
- }
- /// <summary>
- /// Synchronizes two source and target based on the information
- /// stored in the e parameter. This method uses Converter function to convert items stored in argument parameter.
- /// </summary>
- /// <param name="e">The arguments for synchronization.</param>
- /// <param name="source">The source.</param>
- /// <param name="target">The target.</param>
- /// <param name="itemConverter">Function that converts items from argument collection.</param>
- public static void Synchronize(NotifyCollectionChangedEventArgs e, IEnumerable source, IList target, Func<object, object> itemConverter)
- {
- if (source == null || target == null)
- {
- return;
- }
- switch (e.Action)
- {
- case NotifyCollectionChangedAction.Add:
- CollectionHelper.Insert(target, e.NewItems.OfType<object>().Select(c => itemConverter(c)), e.NewStartingIndex, EqualityComparer<object>.Default);
- break;
- case NotifyCollectionChangedAction.Remove:
- CollectionHelper.Remove(target, e.OldItems.OfType<object>().Select(c => itemConverter(c)), EqualityComparer<object>.Default);
- break;
- case NotifyCollectionChangedAction.Replace:
- EnsureAllHaveOneElement(
- e.NewItems.OfType<object>().Select(c => itemConverter(c)),
- e.OldItems.OfType<object>().Select(c => itemConverter(c)));
- Replace(
- target,
- e.NewItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0),
- e.OldItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0),
- EqualityComparer<object>.Default);
- break;
- case NotifyCollectionChangedAction.Reset:
- Reset(source, target, itemConverter);
- break;
- #if WPF
- case NotifyCollectionChangedAction.Move:
- EnsureAllHaveOneElement(e.NewItems, e.OldItems.OfType<object>().Select(c => itemConverter(c)));
- Move(target, e.NewItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0), e.NewStartingIndex);
- break;
- #endif
- }
- }
- internal static void SynchronizeRadCollection<T>(NotifyCollectionChangedEventArgs e,
- IEnumerable source,
- RadObservableCollection<T> target)
- {
- if (source == null || target == null)
- {
- return;
- }
- bool shouldSuspendNotifications = e.Action == NotifyCollectionChangedAction.Reset;
- if (shouldSuspendNotifications)
- {
- target.SuspendNotifications();
- }
- Synchronize(e, source, target);
- if (shouldSuspendNotifications)
- {
- target.ResumeNotifications();
- }
- }
- /// <summary>
- /// Synchronizes two source and target based on the information
- /// stored in the e parameter. This method uses Converter function to convert items stored in argument parameter.
- /// </summary>
- /// <param name="e">The arguments for synchronization.</param>
- /// <param name="source">The source.</param>
- /// <param name="target">The target.</param>
- /// <param name="itemConverter">Function that converts items from argument collection.</param>
- /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
- public static void Synchronize(NotifyCollectionChangedEventArgs e, IEnumerable source, IList target, Func<object, object> itemConverter, IEqualityComparer itemComparer)
- {
- if (source == null || target == null)
- {
- return;
- }
- switch (e.Action)
- {
- case NotifyCollectionChangedAction.Add:
- CollectionHelper.Insert(target, e.NewItems.OfType<object>().Select(c => itemConverter(c)), e.NewStartingIndex, itemComparer);
- break;
- case NotifyCollectionChangedAction.Remove:
- CollectionHelper.Remove(target, e.OldItems.OfType<object>().Select(c => itemConverter(c)), itemComparer);
- break;
- case NotifyCollectionChangedAction.Replace:
- EnsureAllHaveOneElement(
- e.NewItems.OfType<object>().Select(c => itemConverter(c)),
- e.OldItems.OfType<object>().Select(c => itemConverter(c)));
- Replace(
- target,
- e.NewItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0),
- e.OldItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0),
- itemComparer);
- break;
- case NotifyCollectionChangedAction.Reset:
- Reset(source, target, itemConverter);
- break;
- #if WPF
- case NotifyCollectionChangedAction.Move:
- EnsureAllHaveOneElement(e.NewItems, e.OldItems.OfType<object>().Select(c => itemConverter(c)));
- Move(target, e.NewItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0), e.NewStartingIndex);
- break;
- #endif
- }
- }
- /// <summary>
- /// Search for the input element in the collection using itemComparer.
- /// </summary>
- /// <param name="collection">The collection to search in.</param>
- /// <param name="element">Searched element.</param>
- /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
- /// <returns>Element if found, otherwise null.</returns>
- public static object FindEqualElement(IEnumerable collection, object element, IEqualityComparer itemComparer)
- {
- foreach (var item in collection)
- {
- if (itemComparer.Equals(item, element))
- {
- return item;
- }
- }
- return null;
- }
- /// <summary>
- /// Search for the input element in the collection using itemComparer.
- /// </summary>
- /// <param name="collection">The collection to search in.</param>
- /// <param name="element">Searched element.</param>
- /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
- /// <returns>Elements if found, otherwise empty.</returns>
- public static object[] FindEqualElements(IEnumerable collection, object element, IEqualityComparer itemComparer)
- {
- List<object> result = new List<object>();
- foreach (var item in collection)
- {
- if (itemComparer.Equals(item, element))
- {
- result.Add(item);
- }
- }
- return result.ToArray();
- }
- /// <summary>
- /// Inserts newItem in target at the specified index. If the index is
- /// invalid then it simply adds it to target.
- /// </summary>
- /// <param name="target">The list to insert in.</param>
- /// <param name="newItem">The item to insert.</param>
- /// <param name="index">The index at which the item will be inserted.</param>
- public static void Insert(IList target, object newItem, int index)
- {
- CollectionHelper.Insert(target, new[] { newItem }, index, EqualityComparer<object>.Default);
- }
- /// <summary>
- /// Inserts newItems in target at the starting from the specified index.
- /// If the index is invalid then it simply adds them to target.
- /// </summary>
- /// <param name="target">The list to insert in.</param>
- /// <param name="newItems">The items to insert.</param>
- /// <param name="startingIndex">The starting index.</param>
- /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
- public static void Insert(IList target, IEnumerable newItems, int startingIndex, IEqualityComparer itemComparer)
- {
- foreach (var newItem in newItems)
- {
- if (FindEqualElement(target, newItem, itemComparer) != null) continue;
- try
- {
- if (startingIndex >= 0 && startingIndex <= target.Count)
- {
- target.Insert(startingIndex++, newItem);
- }
- else
- {
- target.Add(newItem);
- }
- }
- catch (ArgumentOutOfRangeException)
- {
- target.Add(newItem);
- }
- }
- }
- /// <summary>
- /// Removes items from target.
- /// </summary>
- /// <param name="target">The target from which to remove.</param>
- /// <param name="items">The items to remove.</param>
- /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
- public static void Remove(IList target, IEnumerable items, IEqualityComparer itemComparer)
- {
- foreach (var oldItem in items)
- {
- // remove all equals items including duplicates
- object[] itemsToRemove = FindEqualElements(target, oldItem, itemComparer);
- foreach (var item in itemsToRemove)
- {
- target.Remove(item);
- }
- }
- }
- /// <summary>
- /// Replaces oldItem with newItem in target. If target does not contain
- /// oldItem the it simply adds newItem to target.
- /// </summary>
- /// <param name="target">The target to replace in.</param>
- /// <param name="newItem">The new item.</param>
- /// <param name="oldItem">The old item.</param>
- /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
- /// <remarks>
- /// Replace is kind of tricky when the two collections are different.
- /// Imagine that source is [0, 1] and target is [1, 0] and we have
- /// replaced the 0 from the source with 2. The source has become [2, 1]
- /// We will receive:
- /// target = [1, 0]
- /// newItems = {2}
- /// newStartingIndex = 0 => this is base on the source collection!!!
- /// oldItems = {0}
- /// Now what should we do? Replace target[newStartingIndex] with 3. NO!
- /// If we do this the target will become [3, 0] and that is wrong.
- /// We have to at least try to locate the 0 in the target and replace it
- /// with the 3.
- /// If we cannot find it I think that we should do nothing! Replace should
- /// replace an existing item and if it is not there, then do nothing.
- /// </remarks>
- public static void Replace(IList target, object newItem, object oldItem, IEqualityComparer itemComparer)
- {
- object item = FindEqualElement(target, oldItem, itemComparer);
- int oldItemIndexInTargetCollection = -1;
- if (item != null)
- {
- oldItemIndexInTargetCollection = target.IndexOf(oldItem);
- }
- if (oldItemIndexInTargetCollection != -1)
- {
- // Yey! We found it.
- target[oldItemIndexInTargetCollection] = newItem;
- }
- else
- {
- // Sad, but we cannot replace it since we could not find it in the target.
- // Do it the village way and simply add the new item.
- target.Add(newItem);
- }
- }
- /// <summary>
- /// Makes target equal to source.
- /// </summary>
- /// <param name="source">Source collection.</param>
- /// <param name="target">Target collection.</param>
- public static void Reset(IEnumerable source, IList target)
- {
- CollectionHelper.Reset(source, target, null);
- }
- /// <summary>
- /// Makes target equal to source.
- /// </summary>
- /// <param name="source">Source collection.</param>
- /// <param name="target">Target collection.</param>
- /// <param name="itemConverter">Function that converts items from argument collection.</param>
- public static void Reset(IEnumerable source, IList target, Func<object, object> itemConverter)
- {
- var suspendNotifications = target as ISuspendNotifications;
- if (suspendNotifications != null)
- {
- suspendNotifications.SuspendNotifications();
- }
- try
- {
- target.Clear();
- }
- catch (NotSupportedException)
- {
- }
- if (itemConverter != null)
- {
- CollectionHelper.Insert(target, source.OfType<object>().Select(c => itemConverter(c)), 0, EqualityComparer<object>.Default);
- }
- else
- {
- CollectionHelper.Insert(target, source, 0, EqualityComparer<object>.Default);
- }
- if (suspendNotifications != null)
- {
- suspendNotifications.ResumeNotifications();
- }
- }
- /// <summary>
- /// Moves item to newIndex in target if it is present in target.
- /// Otherwise does nothing.
- /// </summary>
- /// <param name="target">The target to move in.</param>
- /// <param name="item">The item to move.</param>
- /// <param name="newIndex">The index to move the item to.</param>
- public static void Move(IList target, object item, int newIndex)
- {
- if (target.IndexOf(item) != -1)
- {
- // Yey! We found it. Do the job.
- target.Remove(item);
- CollectionHelper.Insert(target, item, newIndex);
- }
- }
- /// <summary>
- /// Raises an exception if one of the enumerables does not have
- /// exactly one element.
- /// </summary>
- /// <param name="enumerables">The enumerables to check.</param>
- public static void EnsureAllHaveOneElement(params IEnumerable[] enumerables)
- {
- foreach (IEnumerable enumerable in enumerables)
- {
- if (enumerable.Count() != 1)
- {
- throw new NotSupportedException("Action Replace with more than one item is not supported.");
- }
- }
- }
- internal static bool AreEquivalent<T>(IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> equalityComparer)
- {
- if (first.Count() != second.Count())
- {
- return false;
- }
- var firstList = first.ToList();
- foreach (var item in second)
- {
- var itemIndex = FindIndex(firstList, i => equalityComparer(i, item));
- if (itemIndex >= 0)
- {
- firstList.RemoveAt(itemIndex);
- }
- else
- {
- return false;
- }
- }
- return true;
- }
- private static int FindIndex<T>(IEnumerable<T> source, Predicate<T> match)
- {
- int index = 0;
- foreach (var item in source)
- {
- if (match(item))
- {
- return index;
- }
- index++;
- }
- return -1;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment