andrew4582

Collection Helper

Jul 6th, 2013
227
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 21.14 KB | None | 0 0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. using System.Linq;
  6. using System.Windows;
  7. using System.Linq.Expressions;
  8.  
  9. namespace Collections
  10. {
  11.     internal static class CollectionHelper
  12.     {
  13.         /// <summary>
  14.         /// Combines the left and right into a new list and
  15.         /// makes left and right to be the same as the new list.
  16.         /// </summary>
  17.         /// <param name="left">The left list.</param>
  18.         /// <param name="right">The right list.</param>
  19.         /// <param name="comparer">The item equality comparer.</param>
  20.         public static void Equalize(IList left, IList right, IEqualityComparer comparer)
  21.         {
  22.             if (left.IsReadOnly || right.IsReadOnly)
  23.             {
  24.                 return;
  25.             }
  26.  
  27.             List<object> combined = new List<object>();
  28.  
  29.             if (left != null && left.Count > 0)
  30.             {
  31.                 combined.AddRange(left.OfType<object>());
  32.             }
  33.  
  34.             if (right != null && right.Count > 0)
  35.             {
  36.                 combined.AddRange(right.OfType<object>());
  37.             }
  38.  
  39.             foreach (var o in combined)
  40.             {
  41.                 if (left != null && !left.Any(item => comparer.Equals(item, o)))
  42.                 {
  43.                     left.Add(o);
  44.                 }
  45.  
  46.                 if (right != null && !right.Any(item => comparer.Equals(item, o)))
  47.                 {
  48.                     right.Add(o);
  49.                 }
  50.             }
  51.         }
  52.  
  53.         /// <summary>
  54.         /// Combines the left and right into a new list and
  55.         /// makes left and right to be the same as the new list.
  56.         /// </summary>
  57.         /// <param name="left">The left list.</param>
  58.         /// <param name="right">The right list.</param>
  59.         public static void Equalize(IList left, IList right)
  60.         {
  61.             Equalize(left, right, EqualityComparer<object>.Default);
  62.         }
  63.  
  64.         /// <summary>
  65.         /// Makes the target collection a mirror copy of the source, so that they both contain the same items.
  66.         /// </summary>
  67.         /// <param name="target">The target collection.</param>
  68.         /// <param name="source">The source enumerable.</param>
  69.         /// <param name="comparer">The item equality comparer.</param>
  70.         public static void Mirror(IList target, IEnumerable source, IEqualityComparer comparer)
  71.         {
  72.             if (target == null || source == null || target.IsReadOnly)
  73.             {
  74.                 return;
  75.             }
  76.  
  77.             foreach (var item in source)
  78.             {
  79.                 if (!target.Any(o => comparer.Equals(item, o)))
  80.                 {
  81.                     target.Add(item);
  82.                 }
  83.             }
  84.  
  85.             foreach (var item in target.ToObjectList())
  86.             {
  87.                 if (!source.Any(o => comparer.Equals(item, o)))
  88.                 {
  89.                     target.Remove(item);
  90.                 }
  91.             }
  92.         }
  93.  
  94.         /// <summary>
  95.         /// Makes the target collection a mirror copy of the source, so that they both contain the same items.
  96.         /// </summary>
  97.         /// <param name="target">The target collection.</param>
  98.         /// <param name="source">The source enumerable.</param>
  99.         public static void Mirror(IList target, IEnumerable source)
  100.         {
  101.             Mirror(target, source, EqualityComparer<object>.Default);
  102.         }
  103.  
  104.         /// <summary>
  105.         /// Synchronizes two source and target based on the information
  106.         /// stored in the e parameter.
  107.         /// </summary>
  108.         /// <param name="e">The arguments for synchronization.</param>
  109.         /// <param name="source">The source.</param>
  110.         /// <param name="target">The target.</param>
  111.         public static void Synchronize(NotifyCollectionChangedEventArgs e, IEnumerable source, IList target)
  112.         {
  113.             if (e == null || source == null || target == null)
  114.             {
  115.                 return;
  116.             }
  117.  
  118.             switch (e.Action)
  119.             {
  120.                 case NotifyCollectionChangedAction.Add:
  121.                     CollectionHelper.Insert(target, e.NewItems, e.NewStartingIndex, EqualityComparer<object>.Default);
  122.                     break;
  123.                 case NotifyCollectionChangedAction.Remove:
  124.                     CollectionHelper.Remove(target, e.OldItems, EqualityComparer<object>.Default);
  125.                     break;
  126.                 case NotifyCollectionChangedAction.Replace:
  127.                     EnsureAllHaveOneElement(e.NewItems, e.OldItems);
  128.                     Replace(target, e.NewItems.ElementAt(0), e.OldItems.ElementAt(0), EqualityComparer<object>.Default);
  129.                     break;
  130.                 case NotifyCollectionChangedAction.Reset:
  131.                     Reset(source, target);
  132.                     break;
  133. #if WPF
  134.                 case NotifyCollectionChangedAction.Move:
  135.                     EnsureAllHaveOneElement(e.NewItems, e.OldItems);
  136.                     Move(target, e.NewItems.ElementAt(0), e.NewStartingIndex);
  137.                     break;
  138. #endif
  139.             }
  140.         }
  141.  
  142.         /// <summary>
  143.         /// Synchronizes two source and target based on the information
  144.         /// stored in the e parameter. This method uses Converter function to convert items stored in argument parameter.
  145.         /// </summary>
  146.         /// <param name="e">The arguments for synchronization.</param>
  147.         /// <param name="source">The source.</param>
  148.         /// <param name="target">The target.</param>
  149.         /// <param name="itemConverter">Function that converts items from argument collection.</param>
  150.         public static void Synchronize(NotifyCollectionChangedEventArgs e, IEnumerable source, IList target, Func<object, object> itemConverter)
  151.         {
  152.             if (source == null || target == null)
  153.             {
  154.                 return;
  155.             }
  156.  
  157.             switch (e.Action)
  158.             {
  159.                 case NotifyCollectionChangedAction.Add:
  160.                     CollectionHelper.Insert(target, e.NewItems.OfType<object>().Select(c => itemConverter(c)), e.NewStartingIndex, EqualityComparer<object>.Default);
  161.                     break;
  162.                 case NotifyCollectionChangedAction.Remove:
  163.                     CollectionHelper.Remove(target, e.OldItems.OfType<object>().Select(c => itemConverter(c)), EqualityComparer<object>.Default);
  164.                     break;
  165.                 case NotifyCollectionChangedAction.Replace:
  166.                     EnsureAllHaveOneElement(
  167.                         e.NewItems.OfType<object>().Select(c => itemConverter(c)),
  168.                         e.OldItems.OfType<object>().Select(c => itemConverter(c)));
  169.  
  170.                     Replace(
  171.                         target,
  172.                         e.NewItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0),
  173.                         e.OldItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0),
  174.                         EqualityComparer<object>.Default);
  175.                     break;
  176.                 case NotifyCollectionChangedAction.Reset:
  177.                     Reset(source, target, itemConverter);
  178.                     break;
  179. #if WPF
  180.                 case NotifyCollectionChangedAction.Move:
  181.                     EnsureAllHaveOneElement(e.NewItems, e.OldItems.OfType<object>().Select(c => itemConverter(c)));
  182.                     Move(target, e.NewItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0), e.NewStartingIndex);
  183.                     break;
  184. #endif
  185.             }
  186.         }
  187.  
  188.         internal static void SynchronizeRadCollection<T>(NotifyCollectionChangedEventArgs e,
  189.             IEnumerable source,
  190.             RadObservableCollection<T> target)
  191.         {
  192.             if (source == null || target == null)
  193.             {
  194.                 return;
  195.             }
  196.             bool shouldSuspendNotifications = e.Action == NotifyCollectionChangedAction.Reset;
  197.  
  198.             if (shouldSuspendNotifications)
  199.             {
  200.                 target.SuspendNotifications();
  201.             }
  202.             Synchronize(e, source, target);
  203.  
  204.             if (shouldSuspendNotifications)
  205.             {
  206.                 target.ResumeNotifications();
  207.             }
  208.         }
  209.  
  210.         /// <summary>
  211.         /// Synchronizes two source and target based on the information
  212.         /// stored in the e parameter. This method uses Converter function to convert items stored in argument parameter.
  213.         /// </summary>
  214.         /// <param name="e">The arguments for synchronization.</param>
  215.         /// <param name="source">The source.</param>
  216.         /// <param name="target">The target.</param>
  217.         /// <param name="itemConverter">Function that converts items from argument collection.</param>
  218.         /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
  219.         public static void Synchronize(NotifyCollectionChangedEventArgs e, IEnumerable source, IList target, Func<object, object> itemConverter, IEqualityComparer itemComparer)
  220.         {
  221.             if (source == null || target == null)
  222.             {
  223.                 return;
  224.             }
  225.  
  226.             switch (e.Action)
  227.             {
  228.                 case NotifyCollectionChangedAction.Add:
  229.                     CollectionHelper.Insert(target, e.NewItems.OfType<object>().Select(c => itemConverter(c)), e.NewStartingIndex, itemComparer);
  230.                     break;
  231.                 case NotifyCollectionChangedAction.Remove:
  232.                     CollectionHelper.Remove(target, e.OldItems.OfType<object>().Select(c => itemConverter(c)), itemComparer);
  233.                     break;
  234.                 case NotifyCollectionChangedAction.Replace:
  235.                     EnsureAllHaveOneElement(
  236.                         e.NewItems.OfType<object>().Select(c => itemConverter(c)),
  237.                         e.OldItems.OfType<object>().Select(c => itemConverter(c)));
  238.  
  239.                     Replace(
  240.                         target,
  241.                         e.NewItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0),
  242.                         e.OldItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0),
  243.                         itemComparer);
  244.                     break;
  245.                 case NotifyCollectionChangedAction.Reset:
  246.                     Reset(source, target, itemConverter);
  247.                     break;
  248. #if WPF
  249.                 case NotifyCollectionChangedAction.Move:
  250.                     EnsureAllHaveOneElement(e.NewItems, e.OldItems.OfType<object>().Select(c => itemConverter(c)));
  251.                     Move(target, e.NewItems.OfType<object>().Select(c => itemConverter(c)).ElementAt(0), e.NewStartingIndex);
  252.                     break;
  253. #endif
  254.             }
  255.         }
  256.  
  257.         /// <summary>
  258.         /// Search for the input element in the collection using itemComparer.
  259.         /// </summary>
  260.         /// <param name="collection">The collection to search in.</param>
  261.         /// <param name="element">Searched element.</param>
  262.         /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
  263.         /// <returns>Element if found, otherwise null.</returns>
  264.         public static object FindEqualElement(IEnumerable collection, object element, IEqualityComparer itemComparer)
  265.         {
  266.             foreach (var item in collection)
  267.             {
  268.                 if (itemComparer.Equals(item, element))
  269.                 {
  270.                     return item;
  271.                 }
  272.             }
  273.             return null;
  274.         }
  275.  
  276.         /// <summary>
  277.         /// Search for the input element in the collection using itemComparer.
  278.         /// </summary>
  279.         /// <param name="collection">The collection to search in.</param>
  280.         /// <param name="element">Searched element.</param>
  281.         /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
  282.         /// <returns>Elements if found, otherwise empty.</returns>
  283.         public static object[] FindEqualElements(IEnumerable collection, object element, IEqualityComparer itemComparer)
  284.         {
  285.             List<object> result = new List<object>();
  286.             foreach (var item in collection)
  287.             {
  288.                 if (itemComparer.Equals(item, element))
  289.                 {
  290.                     result.Add(item);
  291.                 }
  292.             }
  293.             return result.ToArray();
  294.         }
  295.  
  296.         /// <summary>
  297.         /// Inserts newItem in target at the specified index. If the index is
  298.         /// invalid then it simply adds it to target.
  299.         /// </summary>
  300.         /// <param name="target">The list to insert in.</param>
  301.         /// <param name="newItem">The item to insert.</param>
  302.         /// <param name="index">The index at which the item will be inserted.</param>
  303.         public static void Insert(IList target, object newItem, int index)
  304.         {
  305.             CollectionHelper.Insert(target, new[] { newItem }, index, EqualityComparer<object>.Default);
  306.         }
  307.  
  308.         /// <summary>
  309.         /// Inserts newItems in target at the starting from the specified index.
  310.         /// If the index is invalid then it simply adds them to target.
  311.         /// </summary>
  312.         /// <param name="target">The list to insert in.</param>
  313.         /// <param name="newItems">The items to insert.</param>
  314.         /// <param name="startingIndex">The starting index.</param>
  315.         /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
  316.         public static void Insert(IList target, IEnumerable newItems, int startingIndex, IEqualityComparer itemComparer)
  317.         {
  318.             foreach (var newItem in newItems)
  319.             {
  320.                 if (FindEqualElement(target, newItem, itemComparer) != null) continue;
  321.  
  322.                 try
  323.                 {
  324.                     if (startingIndex >= 0 && startingIndex <= target.Count)
  325.                     {
  326.                         target.Insert(startingIndex++, newItem);
  327.                     }
  328.                     else
  329.                     {
  330.                         target.Add(newItem);
  331.                     }
  332.                 }
  333.                 catch (ArgumentOutOfRangeException)
  334.                 {
  335.                     target.Add(newItem);
  336.                 }
  337.             }
  338.         }
  339.  
  340.         /// <summary>
  341.         /// Removes items from target.
  342.         /// </summary>
  343.         /// <param name="target">The target from which to remove.</param>
  344.         /// <param name="items">The items to remove.</param>
  345.         /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
  346.         public static void Remove(IList target, IEnumerable items, IEqualityComparer itemComparer)
  347.         {
  348.             foreach (var oldItem in items)
  349.             {
  350.                 // remove all equals items including duplicates
  351.                 object[] itemsToRemove = FindEqualElements(target, oldItem, itemComparer);
  352.                 foreach (var item in itemsToRemove)
  353.                 {
  354.                     target.Remove(item);
  355.                 }
  356.             }
  357.         }
  358.  
  359.         /// <summary>
  360.         /// Replaces oldItem with newItem in target. If target does not contain
  361.         /// oldItem the it simply adds newItem to target.
  362.         /// </summary>
  363.         /// <param name="target">The target to replace in.</param>
  364.         /// <param name="newItem">The new item.</param>
  365.         /// <param name="oldItem">The old item.</param>
  366.         /// <param name="itemComparer">IEqualityComparer used to compare items.</param>
  367.         /// <remarks>
  368.         /// Replace is kind of tricky when the two collections are different.
  369.         /// Imagine that source is [0, 1] and target is [1, 0] and we have
  370.         /// replaced the 0 from the source with 2. The source has become [2, 1]
  371.         /// We will receive:
  372.         ///      target =            [1, 0]
  373.         ///      newItems =          {2}
  374.         ///      newStartingIndex =  0 => this is base on the source collection!!!
  375.         ///      oldItems =          {0}      
  376.         /// Now what should we do? Replace target[newStartingIndex] with 3. NO!
  377.         /// If we do this the target will become [3, 0] and that is wrong.
  378.         /// We have to at least try to locate the 0 in the target and replace it
  379.         /// with the 3.
  380.         /// If we cannot find it I think that we should do nothing! Replace should
  381.         /// replace an existing item and if it is not there, then do nothing.
  382.         /// </remarks>
  383.         public static void Replace(IList target, object newItem, object oldItem, IEqualityComparer itemComparer)
  384.         {
  385.             object item = FindEqualElement(target, oldItem, itemComparer);
  386.             int oldItemIndexInTargetCollection = -1;
  387.             if (item != null)
  388.             {
  389.                 oldItemIndexInTargetCollection = target.IndexOf(oldItem);
  390.             }
  391.  
  392.             if (oldItemIndexInTargetCollection != -1)
  393.             {
  394.                 // Yey! We found it.
  395.                 target[oldItemIndexInTargetCollection] = newItem;
  396.             }
  397.             else
  398.             {
  399.                 // Sad, but we cannot replace it since we could not find it in the target.
  400.                 // Do it the village way and simply add the new item.
  401.                 target.Add(newItem);
  402.             }
  403.         }
  404.  
  405.         /// <summary>
  406.         /// Makes target equal to source.
  407.         /// </summary>
  408.         /// <param name="source">Source collection.</param>
  409.         /// <param name="target">Target collection.</param>
  410.         public static void Reset(IEnumerable source, IList target)
  411.         {
  412.             CollectionHelper.Reset(source, target, null);
  413.         }
  414.  
  415.         /// <summary>
  416.         /// Makes target equal to source.
  417.         /// </summary>
  418.         /// <param name="source">Source collection.</param>
  419.         /// <param name="target">Target collection.</param>
  420.         /// <param name="itemConverter">Function that converts items from argument collection.</param>
  421.         public static void Reset(IEnumerable source, IList target, Func<object, object> itemConverter)
  422.         {
  423.             var suspendNotifications = target as ISuspendNotifications;
  424.             if (suspendNotifications != null)
  425.             {
  426.                 suspendNotifications.SuspendNotifications();
  427.             }
  428.  
  429.             try
  430.             {
  431.                 target.Clear();
  432.             }
  433.             catch (NotSupportedException)
  434.             {
  435.             }
  436.  
  437.             if (itemConverter != null)
  438.             {
  439.                 CollectionHelper.Insert(target, source.OfType<object>().Select(c => itemConverter(c)), 0, EqualityComparer<object>.Default);
  440.             }
  441.             else
  442.             {
  443.                 CollectionHelper.Insert(target, source, 0, EqualityComparer<object>.Default);
  444.             }
  445.  
  446.             if (suspendNotifications != null)
  447.             {
  448.                 suspendNotifications.ResumeNotifications();
  449.             }
  450.         }
  451.  
  452.         /// <summary>
  453.         /// Moves item to newIndex in target if it is present in target.
  454.         /// Otherwise does nothing.
  455.         /// </summary>
  456.         /// <param name="target">The target to move in.</param>
  457.         /// <param name="item">The item to move.</param>
  458.         /// <param name="newIndex">The index to move the item to.</param>
  459.         public static void Move(IList target, object item, int newIndex)
  460.         {
  461.             if (target.IndexOf(item) != -1)
  462.             {
  463.                 // Yey! We found it. Do the job.
  464.                 target.Remove(item);
  465.                 CollectionHelper.Insert(target, item, newIndex);
  466.             }
  467.         }
  468.  
  469.         /// <summary>
  470.         /// Raises an exception if one of the enumerables does not have
  471.         /// exactly one element.
  472.         /// </summary>
  473.         /// <param name="enumerables">The enumerables to check.</param>
  474.         public static void EnsureAllHaveOneElement(params IEnumerable[] enumerables)
  475.         {
  476.             foreach (IEnumerable enumerable in enumerables)
  477.             {
  478.                 if (enumerable.Count() != 1)
  479.                 {
  480.                     throw new NotSupportedException("Action Replace with more than one item is not supported.");
  481.                 }
  482.             }
  483.         }
  484.  
  485.         internal static bool AreEquivalent<T>(IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> equalityComparer)
  486.         {
  487.             if (first.Count() != second.Count())
  488.             {
  489.                 return false;
  490.             }
  491.  
  492.             var firstList = first.ToList();
  493.             foreach (var item in second)
  494.             {
  495.                 var itemIndex = FindIndex(firstList, i => equalityComparer(i, item));
  496.                 if (itemIndex >= 0)
  497.                 {
  498.                     firstList.RemoveAt(itemIndex);
  499.                 }
  500.                 else
  501.                 {
  502.                     return false;
  503.                 }
  504.             }
  505.  
  506.             return true;
  507.         }
  508.  
  509.         private static int FindIndex<T>(IEnumerable<T> source, Predicate<T> match)
  510.         {
  511.             int index = 0;
  512.             foreach (var item in source)
  513.             {
  514.                 if (match(item))
  515.                 {
  516.                     return index;
  517.                 }
  518.                 index++;
  519.             }
  520.  
  521.             return -1;
  522.         }
  523.     }
  524. }
Advertisement
Add Comment
Please, Sign In to add comment