//******************************
// Written by Peter Golde
// Copyright (c) 2004-2005, Wintellect
//
// Use and restribution of this code is subject to the license agreement
// contained in the file "License.txt" accompanying this file.
//******************************
using System;
using System.Collections;
using System.Collections.Generic;
#pragma warning disable 419 // Ambigious cref in XML comment
// Make internals of this library available to the unit test framework.
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
// Everything should be CLS compliant.
[assembly: CLSCompliant(true)]
namespace Wintellect.PowerCollections
{
///
/// The BinaryPredicate delegate type encapsulates a method that takes two
/// items of the same type, and returns a boolean value representating
/// some relationship between them. For example, checking whether two
/// items are equal or equivalent is one kind of binary predicate.
///
/// The first item.
/// The second item.
/// Whether item1 and item2 satisfy the relationship that the BinaryPredicate defines.
public delegate bool BinaryPredicate(T item1, T item2);
///
/// Algorithms contains a number of static methods that implement
/// algorithms that work on collections. Most of the methods deal with
/// the standard generic collection interfaces such as IEnumerable<T>,
/// ICollection<T> and IList<T>.
///
public static class Algorithms
{
#region Collection wrappers
///
/// The class that is used to implement IList<T> to view a sub-range
/// of a list. The object stores a wrapped list, and a start/count indicating
/// a sub-range of the list. Insertion/deletions through the sub-range view
/// cause the count to change also; insertions and deletions directly on
/// the wrapped list do not.
///
[Serializable]
private class ListRange : ListBase, ICollection
{
private IList wrappedList;
private int start;
private int count;
///
/// Create a sub-range view object on the indicate part
/// of the list.
///
/// List to wrap.
/// The start index of the view in the wrapped list.
/// The number of items in the view.
public ListRange(IList wrappedList, int start, int count)
{
this.wrappedList = wrappedList;
this.start = start;
this.count = count;
}
public override int Count
{
get {
return Math.Min(count, wrappedList.Count - start);
}
}
public override void Clear()
{
if (wrappedList.Count - start < count)
count = wrappedList.Count - start;
while (count > 0) {
wrappedList.RemoveAt(start + count - 1);
--count;
}
}
public override void Insert(int index, T item)
{
if (index < 0 || index > count)
throw new ArgumentOutOfRangeException("index");
wrappedList.Insert(start + index, item);
++count;
}
public override void RemoveAt(int index)
{
if (index < 0 || index >= count)
throw new ArgumentOutOfRangeException("index");
wrappedList.RemoveAt(start + index);
--count;
}
public override bool Remove(T item)
{
if (wrappedList.IsReadOnly)
throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "Range"));
else
return base.Remove(item);
}
public override T this[int index]
{
get
{
if (index < 0 || index >= count)
throw new ArgumentOutOfRangeException("index");
return wrappedList[start + index];
}
set
{
if (index < 0 || index >= count)
throw new ArgumentOutOfRangeException("index");
wrappedList[start + index] = value;
}
}
bool ICollection.IsReadOnly
{
get
{
return wrappedList.IsReadOnly;
}
}
}
///
/// Returns a view onto a sub-range of a list. Items from are not copied; the
/// returned IList<T> is simply a different view onto the same underlying items. Changes to
/// are reflected in the view, and vice versa. Insertions and deletions in the view change the size of the
/// view, but insertions and deletions in the underlying list do not.
///
/// This method can be used to apply an algorithm to a portion of a list. For example:
/// Algorithms.ReverseInPlace(Algorithms.Range(list, 3, 6))
/// will reverse the 6 items beginning at index 3.
/// The type of the items in the list.
/// The list to view.
/// The starting index of the view.
/// The number of items in the view.
/// A list that is a view onto the given sub-list.
/// is null.
/// or is negative.
/// + is greater than the
/// size of .
public static IList Range(IList list, int start, int count)
{
if (list == null)
throw new ArgumentOutOfRangeException("list");
if (start < 0 || start > list.Count || (start == list.Count && count != 0))
throw new ArgumentOutOfRangeException("start");
if (count < 0 || count > list.Count || count + start > list.Count)
throw new ArgumentOutOfRangeException("count");
return new ListRange(list, start, count);
}
///
/// The class that is used to implement IList<T> to view a sub-range
/// of an array. The object stores a wrapped array, and a start/count indicating
/// a sub-range of the array. Insertion/deletions through the sub-range view
/// cause the count to change up to the size of the underlying array. Elements
/// fall off the end of the underlying array.
///
[Serializable]
private class ArrayRange : ListBase
{
private T[] wrappedArray;
private int start;
private int count;
///
/// Create a sub-range view object on the indicate part
/// of the array.
///
/// Array to wrap.
/// The start index of the view in the wrapped list.
/// The number of items in the view.
public ArrayRange(T[] wrappedArray, int start, int count)
{
this.wrappedArray = wrappedArray;
this.start = start;
this.count = count;
}
public override int Count
{
get
{
return count;
}
}
public override void Clear()
{
Array.Copy(wrappedArray, start + count, wrappedArray, start, wrappedArray.Length - (start + count));
Algorithms.FillRange(wrappedArray, wrappedArray.Length - count, count, default(T));
count = 0;
}
public override void Insert(int index, T item)
{
if (index < 0 || index > count)
throw new ArgumentOutOfRangeException("index");
int i = start + index;
if (i + 1 < wrappedArray.Length)
Array.Copy(wrappedArray, i, wrappedArray, i + 1, wrappedArray.Length - i - 1);
if (i < wrappedArray.Length)
wrappedArray[i] = item;
if (start + count < wrappedArray.Length)
++count;
}
public override void RemoveAt(int index)
{
if (index < 0 || index >= count)
throw new ArgumentOutOfRangeException("index");
int i = start + index;
if (i < wrappedArray.Length - 1)
Array.Copy(wrappedArray, i + 1, wrappedArray, i, wrappedArray.Length - i - 1);
wrappedArray[wrappedArray.Length - 1] = default(T);
--count;
}
public override T this[int index]
{
get
{
if (index < 0 || index >= count)
throw new ArgumentOutOfRangeException("index");
return wrappedArray[start + index];
}
set
{
if (index < 0 || index >= count)
throw new ArgumentOutOfRangeException("index");
wrappedArray[start + index] = value;
}
}
}
///
/// Returns a view onto a sub-range of an array. Items from are not copied; the
/// returned IList<T> is simply a different view onto the same underlying items. Changes to
/// are reflected in the view, and vice versa. Insertions and deletions in the view change the size of the
/// view. After an insertion, the last item in "falls off the end". After a deletion, the
/// last item in array becomes the default value (0 or null).
///
/// This method can be used to apply an algorithm to a portion of a array. For example:
/// Algorithms.ReverseInPlace(Algorithms.Range(array, 3, 6))
/// will reverse the 6 items beginning at index 3.
/// The array to view.
/// The starting index of the view.
/// The number of items in the view.
/// A list that is a view onto the given sub-array.
/// is null.
/// or is negative.
/// + is greater than the
/// size of .
public static IList Range(T[] array, int start, int count)
{
if (array == null)
throw new ArgumentOutOfRangeException("array");
if (start < 0 || start > array.Length || (start == array.Length && count != 0))
throw new ArgumentOutOfRangeException("start");
if (count < 0 || count > array.Length || count + start > array.Length)
throw new ArgumentOutOfRangeException("count");
return new ArrayRange(array, start, count);
}
///
/// The read-only ICollection<T> implementation that is used by the ReadOnly method.
/// Methods that modify the collection throw a NotSupportedException, methods that don't
/// modify are fowarded through to the wrapped collection.
///
[Serializable]
private class ReadOnlyCollection : ICollection
{
private ICollection wrappedCollection; // The collection we are wrapping (never null).
///
/// Create a ReadOnlyCollection wrapped around the given collection.
///
/// Collection to wrap.
public ReadOnlyCollection(ICollection wrappedCollection)
{
this.wrappedCollection = wrappedCollection;
}
///
/// Throws an NotSupportedException stating that this collection cannot be modified.
///
private void MethodModifiesCollection()
{
throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "read-only collection"));
}
public IEnumerator GetEnumerator()
{ return wrappedCollection.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return ((IEnumerable)wrappedCollection).GetEnumerator(); }
public bool Contains(T item)
{ return wrappedCollection.Contains(item); }
public void CopyTo(T[] array, int arrayIndex)
{ wrappedCollection.CopyTo(array, arrayIndex); }
public int Count
{
get { return wrappedCollection.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public void Add(T item)
{ MethodModifiesCollection(); }
public void Clear()
{ MethodModifiesCollection(); }
public bool Remove(T item)
{ MethodModifiesCollection(); return false; }
}
///
/// Returns a read-only view onto a collection. The returned ICollection<T> interface
/// only allows operations that do not change the collection: GetEnumerator, Contains, CopyTo,
/// Count. The ReadOnly property returns false, indicating that the collection is read-only. All other
/// methods on the interface throw a NotSupportedException.
///
/// The data in the underlying collection is not copied. If the underlying
/// collection is changed, then the read-only view also changes accordingly.
/// The type of items in the collection.
/// The collection to wrap.
/// A read-only view onto . If is null, then null is returned.
public static ICollection ReadOnly(ICollection collection)
{
if (collection == null)
return null;
else
return new ReadOnlyCollection(collection);
}
///
/// The read-only IList<T> implementation that is used by the ReadOnly method.
/// Methods that modify the list throw a NotSupportedException, methods that don't
/// modify are fowarded through to the wrapped list.
///
[Serializable]
private class ReadOnlyList : IList
{
private IList wrappedList; // The list we are wrapping (never null).
///
/// Create a ReadOnlyList wrapped around the given list.
///
/// List to wrap.
public ReadOnlyList(IList wrappedList)
{
this.wrappedList = wrappedList;
}
///
/// Throws an NotSupportedException stating that this collection cannot be modified.
///
private void MethodModifiesCollection()
{
throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "read-only list"));
}
public IEnumerator GetEnumerator()
{ return wrappedList.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return ((IEnumerable)wrappedList).GetEnumerator(); }
public int IndexOf(T item)
{ return wrappedList.IndexOf(item); }
public bool Contains(T item)
{ return wrappedList.Contains(item); }
public void CopyTo(T[] array, int arrayIndex)
{ wrappedList.CopyTo(array, arrayIndex); }
public int Count
{
get { return wrappedList.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public T this[int index]
{
get { return wrappedList[index]; }
set { MethodModifiesCollection(); }
}
public void Add(T item)
{ MethodModifiesCollection(); }
public void Clear()
{ MethodModifiesCollection(); }
public void Insert(int index, T item)
{ MethodModifiesCollection(); }
public void RemoveAt(int index)
{ MethodModifiesCollection(); }
public bool Remove(T item)
{ MethodModifiesCollection(); return false; }
}
///
/// Returns a read-only view onto a list. The returned IList<T> interface
/// only allows operations that do not change the list: GetEnumerator, Contains, CopyTo,
/// Count, IndexOf, and the get accessor of the indexer.
/// The IsReadOnly property returns true, indicating that the list is read-only. All other
/// methods on the interface throw a NotSupportedException.
///
/// The data in the underlying list is not copied. If the underlying
/// list is changed, then the read-only view also changes accordingly.
/// The type of items in the list.
/// The list to wrap.
/// A read-only view onto . Returns null if is null.
/// If is already read-only, returns .
public static IList ReadOnly(IList list)
{
if (list == null)
return null;
else if (list.IsReadOnly)
return list;
else
return new ReadOnlyList(list);
}
///
/// The private class that implements a read-only wrapped for
/// IDictionary <TKey,TValue>.
///
[Serializable]
private class ReadOnlyDictionary : IDictionary
{
// The dictionary that is wrapped
private IDictionary wrappedDictionary;
///
/// Create a read-only dictionary wrapped around the given dictionary.
///
/// The IDictionary<TKey,TValue> to wrap.
public ReadOnlyDictionary(IDictionary wrappedDictionary)
{
this.wrappedDictionary = wrappedDictionary;
}
///
/// Throws an NotSupportedException stating that this collection cannot be modified.
///
private void MethodModifiesCollection()
{
throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "read-only dictionary"));
}
public void Add(TKey key, TValue value)
{ MethodModifiesCollection(); }
public bool ContainsKey(TKey key)
{ return wrappedDictionary.ContainsKey(key); }
public ICollection Keys
{
get { return ReadOnly(wrappedDictionary.Keys); }
}
public ICollection Values
{
get { return ReadOnly(wrappedDictionary.Values); }
}
public bool Remove(TKey key)
{
MethodModifiesCollection();
return false; // never reached
}
public bool TryGetValue(TKey key, out TValue value)
{ return wrappedDictionary.TryGetValue(key, out value); }
public TValue this[TKey key]
{
get { return wrappedDictionary[key];}
set { MethodModifiesCollection(); }
}
public void Add(KeyValuePair item)
{ MethodModifiesCollection(); }
public void Clear()
{ MethodModifiesCollection(); }
public bool Contains(KeyValuePair item)
{ return wrappedDictionary.Contains(item); }
public void CopyTo(KeyValuePair[] array, int arrayIndex)
{ wrappedDictionary.CopyTo(array, arrayIndex); }
public int Count
{
get { return wrappedDictionary.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(KeyValuePair item)
{
MethodModifiesCollection();
return false; // never reached
}
public IEnumerator> GetEnumerator()
{ return wrappedDictionary.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return ((IEnumerable)wrappedDictionary).GetEnumerator(); }
}
///
/// Returns a read-only view onto a dictionary. The returned IDictionary<TKey,TValue> interface
/// only allows operations that do not change the dictionary.
/// The IsReadOnly property returns true, indicating that the dictionary is read-only. All other
/// methods on the interface throw a NotSupportedException.
///
/// The data in the underlying dictionary is not copied. If the underlying
/// dictionary is changed, then the read-only view also changes accordingly.
/// The dictionary to wrap.
/// A read-only view onto . Returns null if is null.
/// If is already read-only, returns .
public static IDictionary ReadOnly(IDictionary dictionary)
{
if (dictionary == null)
return null;
else if (dictionary.IsReadOnly)
return dictionary;
else
return new ReadOnlyDictionary(dictionary);
}
///
/// The class that provides a typed IEnumerator<T>
/// view onto an untyped IEnumerator interface.
///
[Serializable]
private class TypedEnumerator : IEnumerator
{
private IEnumerator wrappedEnumerator;
///
/// Create a typed IEnumerator<T>
/// view onto an untyped IEnumerator interface
///
/// IEnumerator to wrap.
public TypedEnumerator(IEnumerator wrappedEnumerator)
{
this.wrappedEnumerator = wrappedEnumerator;
}
T IEnumerator.Current
{
get { return (T) wrappedEnumerator.Current; }
}
void IDisposable.Dispose()
{
if (wrappedEnumerator is IDisposable)
((IDisposable)wrappedEnumerator).Dispose();
}
object IEnumerator.Current
{
get { return wrappedEnumerator.Current; }
}
bool IEnumerator.MoveNext()
{
return wrappedEnumerator.MoveNext();
}
void IEnumerator.Reset()
{
wrappedEnumerator.Reset();
}
}
///
/// The class that provides a typed IEnumerable<T> view
/// onto an untyped IEnumerable interface.
///
[Serializable]
private class TypedEnumerable : IEnumerable
{
private IEnumerable wrappedEnumerable;
///
/// Create a typed IEnumerable<T> view
/// onto an untyped IEnumerable interface.
///
/// IEnumerable interface to wrap.
public TypedEnumerable(IEnumerable wrappedEnumerable)
{
this.wrappedEnumerable = wrappedEnumerable;
}
public IEnumerator GetEnumerator()
{
return new TypedEnumerator(wrappedEnumerable.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return wrappedEnumerable.GetEnumerator();
}
}
///
/// Given a non-generic IEnumerable interface, wrap a generic IEnumerable<T>
/// interface around it. The generic interface will enumerate the same objects as the
/// underlying non-generic collection, but can be used in places that require a generic interface.
/// The underlying non-generic collection must contain only items that
/// are of type or a type derived from it. This method is useful
/// when interfacing older, non-generic collections to newer code that uses generic interfaces.
///
/// Some collections implement both generic and non-generic interfaces. For efficiency,
/// this method will first attempt to cast to IEnumerable<T>.
/// If that succeeds, it is returned; otherwise, a wrapper object is created.
/// The item type of the wrapper collection.
/// An untyped collection. This collection should only contain
/// items of type or a type derived from it.
/// A generic IEnumerable<T> wrapper around .
/// If is null, then null is returned.
public static IEnumerable TypedAs(IEnumerable untypedCollection)
{
if (untypedCollection == null)
return null;
else if (untypedCollection is IEnumerable)
return (IEnumerable)untypedCollection;
else
return new TypedEnumerable(untypedCollection);
}
///
/// The class that provides a typed ICollection<T> view
/// onto an untyped ICollection interface. The ICollection<T>
/// is read-only.
///
[Serializable]
private class TypedCollection : ICollection
{
private ICollection wrappedCollection;
///
/// Create a typed ICollection<T> view
/// onto an untyped ICollection interface.
///
/// ICollection interface to wrap.
public TypedCollection(ICollection wrappedCollection)
{
this.wrappedCollection = wrappedCollection;
}
///
/// Throws an NotSupportedException stating that this collection cannot be modified.
///
private void MethodModifiesCollection()
{
throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "strongly-typed Collection"));
}
public void Add(T item)
{ MethodModifiesCollection(); }
public void Clear()
{ MethodModifiesCollection(); }
public bool Remove(T item)
{ MethodModifiesCollection(); return false; }
public bool Contains(T item)
{
IEqualityComparer equalityComparer = EqualityComparer.Default;
foreach (object obj in wrappedCollection) {
if (obj is T && equalityComparer.Equals(item, (T)obj))
return true;
}
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{ wrappedCollection.CopyTo(array, arrayIndex); }
public int Count
{
get { return wrappedCollection.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public IEnumerator GetEnumerator()
{ return new TypedEnumerator(wrappedCollection.GetEnumerator()); }
IEnumerator IEnumerable.GetEnumerator()
{ return wrappedCollection.GetEnumerator(); }
}
///
/// Given a non-generic ICollection interface, wrap a generic ICollection<T>
/// interface around it. The generic interface will enumerate the same objects as the
/// underlying non-generic collection, but can be used in places that require a generic interface.
/// The underlying non-generic collection must contain only items that
/// are of type or a type derived from it. This method is useful
/// when interfacing older, non-generic collections to newer code that uses generic interfaces.
///
/// Some collections implement both generic and non-generic interfaces. For efficiency,
/// this method will first attempt to cast to ICollection<T>.
/// If that succeeds, it is returned; otherwise, a wrapper object is created.
/// Unlike the generic interface, the non-generic ICollection interfaces does
/// not contain methods for adding or removing items from the collection. For this reason,
/// the returned ICollection<T> will be read-only.
/// The item type of the wrapper collection.
/// An untyped collection. This collection should only contain
/// items of type or a type derived from it.
/// A generic ICollection<T> wrapper around .
/// If is null, then null is returned.
public static ICollection TypedAs(ICollection untypedCollection)
{
if (untypedCollection == null)
return null;
else if (untypedCollection is ICollection)
return (ICollection) untypedCollection;
else
return new TypedCollection(untypedCollection);
}
///
/// The class used to create a typed IList<T> view onto
/// an untype IList interface.
///
[Serializable]
private class TypedList : IList
{
private IList wrappedList;
///
/// Create a typed IList<T> view onto
/// an untype IList interface.
///
/// The IList to wrap.
public TypedList(IList wrappedList)
{
this.wrappedList = wrappedList;
}
public IEnumerator GetEnumerator()
{ return new TypedEnumerator(wrappedList.GetEnumerator()); }
IEnumerator IEnumerable.GetEnumerator()
{ return wrappedList.GetEnumerator(); }
public int IndexOf(T item)
{ return wrappedList.IndexOf(item); }
public void Insert(int index, T item)
{ wrappedList.Insert(index, item); }
public void RemoveAt(int index)
{ wrappedList.RemoveAt(index); }
public void Add(T item)
{ wrappedList.Add(item); }
public void Clear()
{ wrappedList.Clear(); }
public bool Contains(T item)
{ return wrappedList.Contains(item); }
public void CopyTo(T[] array, int arrayIndex)
{ wrappedList.CopyTo(array, arrayIndex); }
public T this[int index]
{
get { return (T)wrappedList[index]; }
set { wrappedList[index] = value; }
}
public int Count
{
get { return wrappedList.Count ; }
}
public bool IsReadOnly
{
get { return wrappedList.IsReadOnly; }
}
public bool Remove(T item)
{
if (wrappedList.Contains(item)) {
wrappedList.Remove(item);
return true;
}
else {
return false;
}
}
}
///
/// Given a non-generic IList interface, wrap a generic IList<T>
/// interface around it. The generic interface will enumerate the same objects as the
/// underlying non-generic list, but can be used in places that require a generic interface.
/// The underlying non-generic list must contain only items that
/// are of type or a type derived from it. This method is useful
/// when interfacing older, non-generic lists to newer code that uses generic interfaces.
///
/// Some collections implement both generic and non-generic interfaces. For efficiency,
/// this method will first attempt to cast to IList<T>.
/// If that succeeds, it is returned; otherwise, a wrapper object is created.
/// The item type of the wrapper list.
/// An untyped list. This list should only contain
/// items of type or a type derived from it.
/// A generic IList<T> wrapper around .
/// If is null, then null is returned.
public static IList TypedAs(IList untypedList)
{
if (untypedList == null)
return null;
else if (untypedList is IList)
return (IList)untypedList;
else
return new TypedList(untypedList);
}
///
/// The class that is used to provide an untyped ICollection
/// view onto a typed ICollection<T> interface.
///
[Serializable]
private class UntypedCollection : ICollection
{
private ICollection wrappedCollection;
///
/// Create an untyped ICollection
/// view onto a typed ICollection<T> interface.
///
/// The ICollection<T> to wrap.
public UntypedCollection(ICollection wrappedCollection)
{
this.wrappedCollection = wrappedCollection;
}
public void CopyTo(Array array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
int i = 0;
int count = wrappedCollection.Count;
if (index < 0)
throw new ArgumentOutOfRangeException("index", index, Strings.ArgMustNotBeNegative);
if (index >= array.Length || count > array.Length - index)
throw new ArgumentException("index", Strings.ArrayTooSmall);
foreach (T item in wrappedCollection) {
if (i >= count)
break;
array.SetValue(item, index);
++index;
++i;
}
}
public int Count
{
get { return wrappedCollection.Count; }
}
public bool IsSynchronized
{
get { return false; }
}
public object SyncRoot
{
get { return this; }
}
public IEnumerator GetEnumerator()
{
return ((IEnumerable)wrappedCollection).GetEnumerator();
}
}
///
/// Given a generic ICollection<T> interface, wrap a non-generic (untyped)
/// ICollection interface around it. The non-generic interface will contain the same objects as the
/// underlying generic collection, but can be used in places that require a non-generic interface.
/// This method is useful when interfacing generic interfaces with older code that uses non-generic interfaces.
///
/// Many generic collections already implement the non-generic interfaces directly. This
/// method will first attempt to simply cast to ICollection. If that
/// succeeds, it is returned; if it fails, then a wrapper object is created.
/// The item type of the underlying collection.
/// A typed collection to wrap.
/// A non-generic ICollection wrapper around .
/// If is null, then null is returned.
public static ICollection Untyped(ICollection typedCollection)
{
if (typedCollection == null)
return null;
else if (typedCollection is ICollection)
return (ICollection)typedCollection;
else
return new UntypedCollection(typedCollection);
}
///
/// The class that implements a non-generic IList wrapper
/// around a generic IList<T> interface.
///
[Serializable]
private class UntypedList : IList
{
private IList wrappedList;
///
/// Create a non-generic IList wrapper
/// around a generic IList<T> interface.
///
/// The IList<T> interface to wrap.
public UntypedList(IList wrappedList)
{
this.wrappedList = wrappedList;
}
///
/// Convert the given parameter to T. Throw an ArgumentException
/// if it isn't.
///
/// parameter name
/// parameter value
private T ConvertToItemType(string name, object value)
{
try {
return (T)value;
}
catch (InvalidCastException) {
throw new ArgumentException(string.Format(Strings.WrongType, value, typeof(T)), name);
}
}
public int Add(object value)
{
// We assume that Add always adds to the end. Is this true?
wrappedList.Add(ConvertToItemType("value", value));
return wrappedList.Count - 1;
}
public void Clear()
{ wrappedList.Clear(); }
public bool Contains(object value)
{
if (value is T)
return wrappedList.Contains((T)value);
else
return false;
}
public int IndexOf(object value)
{
if (value is T)
return wrappedList.IndexOf((T)value);
else
return -1;
}
public void Insert(int index, object value)
{ wrappedList.Insert(index, ConvertToItemType("value", value)); }
public bool IsFixedSize
{
get { return false; }
}
public bool IsReadOnly
{
get { return wrappedList.IsReadOnly; }
}
public void Remove(object value)
{
if (value is T)
wrappedList.Remove((T)value);
}
public void RemoveAt(int index)
{ wrappedList.RemoveAt(index);}
public object this[int index]
{
get { return wrappedList[index]; }
set { wrappedList[index] = ConvertToItemType("value", value); }
}
public void CopyTo(Array array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
int i = 0;
int count = wrappedList.Count;
if (index < 0)
throw new ArgumentOutOfRangeException("index", index, Strings.ArgMustNotBeNegative);
if (index >= array.Length || count > array.Length - index)
throw new ArgumentException("index", Strings.ArrayTooSmall);
foreach (T item in wrappedList) {
if (i >= count)
break;
array.SetValue(item, index);
++index;
++i;
}
}
public int Count
{
get { return wrappedList.Count; }
}
public bool IsSynchronized
{
get { return false; }
}
public object SyncRoot
{
get { return this; }
}
public IEnumerator GetEnumerator()
{ return ((IEnumerable)wrappedList).GetEnumerator(); }
}
///
/// Given a generic IList<T> interface, wrap a non-generic (untyped)
/// IList interface around it. The non-generic interface will contain the same objects as the
/// underlying generic list, but can be used in places that require a non-generic interface.
/// This method is useful when interfacing generic interfaces with older code that uses non-generic interfaces.
///
/// Many generic collections already implement the non-generic interfaces directly. This
/// method will first attempt to simply cast to IList. If that
/// succeeds, it is returned; if it fails, then a wrapper object is created.
/// The item type of the underlying list.
/// A typed list to wrap.
/// A non-generic IList wrapper around .
/// If is null, then null is returned.
public static IList Untyped(IList typedList)
{
if (typedList == null)
return null;
else if (typedList is IList)
return (IList)typedList;
else
return new UntypedList(typedList);
}
///
/// The class that is used to implement IList<T> to view an array
/// in a read-write way. Insertions cause the last item in the array
/// to fall off, deletions replace the last item with the default value.
///
[Serializable]
private class ArrayWrapper : ListBase, IList
{
private T[] wrappedArray;
///
/// Create a list wrapper object on an array.
///
/// Array to wrap.
public ArrayWrapper(T[] wrappedArray)
{
this.wrappedArray = wrappedArray;
}
public override int Count
{
get
{
return wrappedArray.Length;
}
}
public override void Clear()
{
int count = wrappedArray.Length;
for (int i = 0; i < count; ++i)
wrappedArray[i] = default(T);
}
public override void Insert(int index, T item)
{
if (index < 0 || index > wrappedArray.Length)
throw new ArgumentOutOfRangeException("index");
if (index + 1 < wrappedArray.Length)
Array.Copy(wrappedArray, index, wrappedArray, index + 1, wrappedArray.Length - index - 1);
if (index < wrappedArray.Length)
wrappedArray[index] = item;
}
public override void RemoveAt(int index)
{
if (index < 0 || index >= wrappedArray.Length)
throw new ArgumentOutOfRangeException("index");
if (index < wrappedArray.Length - 1)
Array.Copy(wrappedArray, index + 1, wrappedArray, index, wrappedArray.Length - index - 1);
wrappedArray[wrappedArray.Length - 1] = default(T);
}
public override T this[int index]
{
get
{
if (index < 0 || index >= wrappedArray.Length)
throw new ArgumentOutOfRangeException("index");
return wrappedArray[index];
}
set
{
if (index < 0 || index >= wrappedArray.Length)
throw new ArgumentOutOfRangeException("index");
wrappedArray[index] = value;
}
}
public override void CopyTo(T[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException("array");
if (array.Length < wrappedArray.Length)
throw new ArgumentException("array is too short", "array");
if (arrayIndex < 0 || arrayIndex >= array.Length)
throw new ArgumentOutOfRangeException("arrayIndex");
if (array.Length + arrayIndex < wrappedArray.Length)
throw new ArgumentOutOfRangeException("arrayIndex");
Array.Copy(wrappedArray, 0, array, arrayIndex, wrappedArray.Length);
}
public override IEnumerator GetEnumerator()
{
return ((IList)wrappedArray).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IList)wrappedArray).GetEnumerator();
}
///
/// Return true, to indicate that the list is fixed size.
///
bool IList.IsFixedSize
{
get
{
return true;
}
}
}
///
/// Creates a read-write IList<T> wrapper around an array. When an array is
/// implicitely converted to an IList<T>, changes to the items in the array cannot
/// be made through the interface. This method creates a read-write IList<T> wrapper
/// on an array that can be used to make changes to the array.
/// Use this method when you need to pass an array to an algorithms that takes an
/// IList<T> and that tries to modify items in the list. Algorithms in this class generally do not
/// need this method, since they have been design to operate on arrays even when they
/// are passed as an IList<T>.
///
/// Since arrays cannot be resized, inserting an item causes the last item in the array to be automatically
/// removed. Removing an item causes the last item in the array to be replaced with a default value (0 or null). Clearing
/// the list causes all the items to be replaced with a default value.
/// The array to wrap.
/// An IList<T> wrapper onto .
public static IList ReadWriteList(T[] array)
{
if (array == null)
throw new ArgumentNullException("array");
return new ArrayWrapper(array);
}
#endregion Collection wrappers
#region Replacing
///
/// Replace all items in a collection equal to a particular value with another values, yielding another collection.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// The collection to process.
/// The value to find and replace within .
/// The new value to replace with.
/// An new collection with the items from , in the same order,
/// with the appropriate replacements made.
public static IEnumerable Replace(IEnumerable collection, T itemFind, T replaceWith)
{
return Replace(collection, itemFind, replaceWith, EqualityComparer.Default);
}
///
/// Replace all items in a collection equal to a particular value with another values, yielding another collection. A passed
/// IEqualityComparer is used to determine equality.
///
/// The collection to process.
/// The value to find and replace within .
/// The new value to replace with.
/// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called.
/// An new collection with the items from , in the same order,
/// with the appropriate replacements made.
public static IEnumerable Replace(IEnumerable collection, T itemFind, T replaceWith, IEqualityComparer equalityComparer)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
foreach (T item in collection) {
if (equalityComparer.Equals(item, itemFind))
yield return replaceWith;
else
yield return item;
}
}
///
/// Replace all items in a collection that a predicate evalues at true with a value, yielding another collection. .
///
/// The collection to process.
/// The predicate used to evaluate items with the collection. If the predicate returns true for a particular
/// item, the item is replaces with .
/// The new value to replace with.
/// An new collection with the items from , in the same order,
/// with the appropriate replacements made.
public static IEnumerable Replace(IEnumerable collection, Predicate predicate, T replaceWith)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (predicate == null)
throw new ArgumentNullException("predicate");
foreach (T item in collection) {
if (predicate(item))
yield return replaceWith;
else
yield return item;
}
}
///
/// Replace all items in a list or array equal to a particular value with another value. The replacement is done in-place, changing
/// the list.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// Although arrays cast to IList<T> are normally read-only, this method
/// will work correctly and modify an array passed as .
/// The list or array to process.
/// The value to find and replace within .
/// The new value to replace with.
public static void ReplaceInPlace(IList list, T itemFind, T replaceWith)
{
ReplaceInPlace(list, itemFind, replaceWith, EqualityComparer.Default);
}
///
/// Replace all items in a list or array equal to a particular value with another values.
/// The replacement is done in-place, changing
/// the list. A passed IEqualityComparer is used to determine equality.
///
/// Although arrays cast to IList<T> are normally read-only, this method
/// will work correctly and modify an array passed as .
/// The list or array to process.
/// The value to find and replace within .
/// The new value to replace with.
/// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called.
public static void ReplaceInPlace(IList list, T itemFind, T replaceWith, IEqualityComparer equalityComparer)
{
if (list == null)
throw new ArgumentNullException("list");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
if (list is T[])
list = new ArrayWrapper((T[])list);
if (list.IsReadOnly)
throw new ArgumentException(Strings.ListIsReadOnly, "list");
int listCount = list.Count;
for (int index = 0; index < listCount; ++index) {
if (equalityComparer.Equals(list[index], itemFind))
list[index] = replaceWith;
}
}
///
/// Replace all items in a list or array that a predicate evaluates at true with a value. The replacement is done in-place, changing
/// the list.
///
/// Although arrays cast to IList<T> are normally read-only, this method
/// will work correctly and modify an array passed as .
/// The list or array to process.
/// The predicate used to evaluate items with the collection. If the predicate returns true for a particular
/// item, the item is replaces with .
/// The new value to replace with.
public static void ReplaceInPlace(IList list, Predicate predicate, T replaceWith)
{
if (list == null)
throw new ArgumentNullException("list");
if (predicate == null)
throw new ArgumentNullException("predicate");
if (list is T[])
list = new ArrayWrapper((T[])list);
if (list.IsReadOnly)
throw new ArgumentException(Strings.ListIsReadOnly, "list");
int listCount = list.Count;
for (int index = 0; index < listCount; ++index) {
if (predicate(list[index]))
list[index] = replaceWith;
}
}
#endregion Replacing
#region Consecutive items
///
/// Remove consecutive equal items from a collection, yielding another collection. In each run of consecutive equal items
/// in the collection, all items after the first item in the run are removed.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// The collection to process.
/// An new collection with the items from , in the same order,
/// with consecutive duplicates removed.
/// is null.
public static IEnumerable RemoveDuplicates(IEnumerable collection)
{
return RemoveDuplicates(collection, EqualityComparer.Default);
}
///
/// Remove consecutive equal items from a collection, yielding another collection. In each run of consecutive equal items
/// in the collection, all items after the first item in the run are removed. A passed
/// IEqualityComparer is used to determine equality.
///
/// The collection to process.
/// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called.
/// An new collection with the items from , in the same order,
/// with consecutive duplicates removed.
/// or is null.
public static IEnumerable RemoveDuplicates(IEnumerable collection, IEqualityComparer equalityComparer)
{
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
return RemoveDuplicates(collection, equalityComparer.Equals);
}
///
/// Remove consecutive "equal" items from a collection, yielding another collection. In each run of consecutive equal items
/// in the collection, all items after the first item in the run are removed. The passed
/// BinaryPredicate is used to determine if two items are "equal".
///
/// Since an arbitrary BinaryPredicate is passed to this function, what is being removed need not be true equality.
/// The collection to process.
/// The BinaryPredicate used to compare items for "equality". An item current is removed if predicate(first, current)==true, where
/// first is the first item in the group of "duplicate" items.
/// An new collection with the items from , in the same order,
/// with consecutive "duplicates" removed.
public static IEnumerable RemoveDuplicates(IEnumerable collection, BinaryPredicate predicate)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (predicate == null)
throw new ArgumentNullException("predicate");
T current = default(T);
bool atBeginning = true;
foreach (T item in collection) {
// Is the new item different from the current item?
if (atBeginning || !predicate(current, item)) {
current = item;
yield return item;
}
atBeginning = false;
}
}
///
/// Remove consecutive equal items from a list or array. In each run of consecutive equal items
/// in the list, all items after the first item in the run are removed. The removal is done in-place, changing
/// the list.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// Although arrays cast to IList<T> are normally read-only, this method
/// will work correctly and modify an array passed as .
/// The list or array to process.
public static void RemoveDuplicatesInPlace(IList list)
{
RemoveDuplicatesInPlace(list, EqualityComparer.Default);
}
///
/// Remove subsequent consecutive equal items from a list or array. In each run of consecutive equal items
/// in the list, all items after the first item in the run are removed.
/// The replacement is done in-place, changing
/// the list. A passed IEqualityComparer is used to determine equality.
///
/// Although arrays cast to IList<T> are normally read-only, this method
/// will work correctly and modify an array passed as .
/// The list or array to process.
/// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called.
public static void RemoveDuplicatesInPlace(IList list, IEqualityComparer equalityComparer)
{
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
RemoveDuplicatesInPlace(list, equalityComparer.Equals);
}
///
/// Remove consecutive "equal" items from a list or array. In each run of consecutive equal items
/// in the list, all items after the first item in the run are removed. The replacement is done in-place, changing
/// the list. The passed BinaryPredicate is used to determine if two items are "equal".
///
/// Since an arbitrary BinaryPredicate is passed to this function, what is being tested for need not be true equality.
/// Although arrays cast to IList<T> are normally read-only, this method
/// will work correctly and modify an array passed as .
/// The list or array to process.
/// The BinaryPredicate used to compare items for "equality".
public static void RemoveDuplicatesInPlace(IList list, BinaryPredicate predicate)
{
if (list == null)
throw new ArgumentNullException("list");
if (predicate == null)
throw new ArgumentNullException("predicate");
if (list is T[])
list = new ArrayWrapper((T[])list);
if (list.IsReadOnly)
throw new ArgumentException(Strings.ListIsReadOnly, "list");
T current = default(T);
T item;
int i = -1, j = 0;
int listCount = list.Count;
// Remove duplicates, compressing items to lower in the list.
while (j < listCount) {
item = list[j];
if (i < 0 || !predicate(current, item)) {
current = item;
++i;
if (i != j)
list[i] = current;
}
++j;
}
++i;
if (i < listCount) {
// remove items from the end.
if (list is ArrayWrapper || (list is IList && ((IList)list).IsFixedSize)) {
// An array or similar. Null out the last elements.
while (i < listCount)
list[i++] = default(T);
}
else {
// Normal list.
while (i < listCount) {
list.RemoveAt(listCount - 1);
--listCount;
}
}
}
}
///
/// Finds the first occurence of consecutive equal items in the
/// list.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// The list to examine.
/// The number of consecutive equal items to look for. The count must be at least 1.
/// The index of the first item in the first run of consecutive equal items, or -1 if no such run exists..
public static int FirstConsecutiveEqual(IList list, int count)
{
return FirstConsecutiveEqual(list, count, EqualityComparer.Default);
}
///
/// Finds the first occurence of consecutive equal items in the
/// list. A passed IEqualityComparer is used to determine equality.
///
/// The list to examine.
/// The number of consecutive equal items to look for. The count must be at least 1.
/// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called.
/// The index of the first item in the first run of consecutive equal items, or -1 if no such run exists.
public static int FirstConsecutiveEqual(IList list, int count, IEqualityComparer equalityComparer)
{
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
return FirstConsecutiveEqual(list, count, equalityComparer.Equals);
}
///
/// Finds the first occurence of consecutive "equal" items in the
/// list. The passed BinaryPredicate is used to determine if two items are "equal".
///
/// Since an arbitrary BinaryPredicate is passed to this function, what is being tested for need not be true equality.
/// The list to examine.
/// The number of consecutive equal items to look for. The count must be at least 1.
/// The BinaryPredicate used to compare items for "equality".
/// The index of the first item in the first run of consecutive equal items, or -1 if no such run exists.
public static int FirstConsecutiveEqual(IList list, int count, BinaryPredicate predicate)
{
if (list == null)
throw new ArgumentNullException("list");
if (predicate == null)
throw new ArgumentNullException("predicate");
if (count < 1)
throw new ArgumentOutOfRangeException("count");
int listCount = list.Count;
if (listCount < count)
return -1; // Can't find run longer than the list itself.
if (count == 1)
return 0; // Run of 1 must be the first item in the list.
int start = 0, index = 0;
T current = default(T);
int runLength = 0;
// Go through the list, looking for a run of the given length.
foreach (T item in list) {
if (index > 0 && predicate(current, item)) {
++runLength;
if (runLength >= count)
return start;
}
else {
current = item;
start = index;
runLength = 1;
}
++index;
}
return -1;
}
///
/// Finds the first occurence of consecutive items in the
/// list for which a given predicate returns true.
///
/// The list to examine.
/// The number of consecutive items to look for. The count must be at least 1.
/// The predicate used to test each item.
/// The index of the first item in the first run of items where
/// returns true for all items in the run, or -1 if no such run exists.
public static int FirstConsecutiveWhere(IList list, int count, Predicate predicate)
{
if (list == null)
throw new ArgumentNullException("list");
if (predicate == null)
throw new ArgumentNullException("predicate");
if (count < 1)
throw new ArgumentOutOfRangeException("count");
int listCount = list.Count;
if (count > listCount)
return -1; // Can't find run longer than the list itself.
int index = 0, start = -1;
int runLength = 0;
// Scan the list in order, looking for the number of consecutive true items.
foreach (T item in list) {
if (predicate(item)) {
if (start < 0)
start = index;
++runLength;
if (runLength >= count)
return start;
}
else {
runLength = 0;
start = -1;
}
++index;
}
return -1;
}
#endregion Consecutive items
#region Find and SearchForSubsequence
///
/// Finds the first item in a collection that satisfies the condition
/// defined by .
///
/// If the default value for T could be present in the collection, and
/// would be matched by the predicate, then this method is inappropriate, because
/// you cannot disguish whether the default value for T was actually present in the collection,
/// or no items matched the predicate. In this case, use TryFindFirstWhere.
/// The collection to search.
/// A delegate that defined the condition to check for.
/// The first item in the collection that matches the condition, or the default value for T (0 or null) if no
/// item that matches the condition is found.
///
public static T FindFirstWhere(IEnumerable collection, Predicate predicate)
{
T retval;
if (Algorithms.TryFindFirstWhere(collection, predicate, out retval))
return retval;
else
return default(T);
}
///
/// Finds the first item in a collection that satisfies the condition
/// defined by .
///
/// The collection to search.
/// A delegate that defined the condition to check for.
/// Outputs the first item in the collection that matches the condition, if the method returns true.
/// True if an item satisfying the condition was found. False if no such item exists in the collection.
///
public static bool TryFindFirstWhere(IEnumerable collection, Predicate predicate, out T foundItem)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (predicate == null)
throw new ArgumentNullException("predicate");
foreach (T item in collection) {
if (predicate(item)) {
foundItem = item;
return true;
}
}
// didn't find any item that matches.
foundItem = default(T);
return false;
}
///
/// Finds the last item in a collection that satisfies the condition
/// defined by .
///
/// If the collection implements IList<T>, then the list is scanned in reverse until a
/// matching item is found. Otherwise, the entire collection is iterated in the forward direction.
/// If the default value for T could be present in the collection, and
/// would be matched by the predicate, then this method is inappropriate, because
/// you cannot disguish whether the default value for T was actually present in the collection,
/// or no items matched the predicate. In this case, use TryFindFirstWhere.
/// The collection to search.
/// A delegate that defined the condition to check for.
/// The last item in the collection that matches the condition, or the default value for T (0 or null) if no
/// item that matches the condition is found.
///
public static T FindLastWhere(IEnumerable collection, Predicate predicate)
{
T retval;
if (Algorithms.TryFindLastWhere(collection, predicate, out retval))
return retval;
else
return default(T);
}
///
/// Finds the last item in a collection that satisfies the condition
/// defined by .
///
/// If the collection implements IList<T>, then the list is scanned in reverse until a
/// matching item is found. Otherwise, the entire collection is iterated in the forward direction.
/// The collection to search.
/// A delegate that defined the condition to check for.
/// Outputs the last item in the collection that matches the condition, if the method returns true.
/// True if an item satisfying the condition was found. False if no such item exists in the collection.
///
public static bool TryFindLastWhere(IEnumerable collection, Predicate predicate, out T foundItem)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (predicate == null)
throw new ArgumentNullException("predicate");
IList list = collection as IList;
if (list != null) {
// If it's a list, we can iterate in reverse.
for (int index = list.Count - 1; index >= 0; --index) {
T item = list[index];
if (predicate(item)) {
foundItem = item;
return true;
}
}
// didn't find any item that matches.
foundItem = default(T);
return false;
}
else {
// Otherwise, iterate the whole thing and remember the last matching one.
bool found = false;
foundItem = default(T);
foreach (T item in collection) {
if (predicate(item)) {
foundItem = item;
found = true;
}
}
return found;
}
}
///
/// Enumerates all the items in that satisfy the condition defined
/// by .
///
/// The collection to check all the items in.
/// A delegate that defines the condition to check for.
/// An IEnumerable<T> that enumerates the items that satisfy the condition.
public static IEnumerable FindWhere(IEnumerable collection, Predicate predicate)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (predicate == null)
throw new ArgumentNullException("predicate");
foreach (T item in collection) {
if (predicate(item)) {
yield return item;
}
}
}
///
/// Finds the index of the first item in a list that satisfies the condition
/// defined by .
///
/// The list to search.
/// A delegate that defined the condition to check for.
/// The index of the first item satisfying the condition. -1 if no such item exists in the list.
public static int FindFirstIndexWhere(IList list, Predicate predicate)
{
if (list == null)
throw new ArgumentNullException("list");
if (predicate == null)
throw new ArgumentNullException("predicate");
int index = 0;
foreach (T item in list) {
if (predicate(item)) {
return index;
}
++index;
}
// didn't find any item that matches.
return -1;
}
///
/// Finds the index of the last item in a list that satisfies the condition
/// defined by .
///
/// The list to search.
/// A delegate that defined the condition to check for.
/// The index of the last item satisfying the condition. -1 if no such item exists in the list.
public static int FindLastIndexWhere(IList list, Predicate predicate)
{
if (list == null)
throw new ArgumentNullException("list");
if (predicate == null)
throw new ArgumentNullException("predicate");
for (int index = list.Count - 1; index >= 0; --index) {
if (predicate(list[index])) {
return index;
}
}
// didn't find any item that matches.
return -1;
}
///
/// Enumerates the indices of all the items in that satisfy the condition defined
/// by .
///
/// The list to check all the items in.
/// A delegate that defines the condition to check for.
/// An IEnumerable<T> that enumerates the indices of items that satisfy the condition.
public static IEnumerable FindIndicesWhere(IList list, Predicate predicate)
{
if (list == null)
throw new ArgumentNullException("list");
if (predicate == null)
throw new ArgumentNullException("predicate");
int index = 0;
foreach (T item in list) {
if (predicate(item)) {
yield return index;
}
++index;
}
}
///
/// Finds the index of the first item in a list equal to a given item.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// The list to search.
/// The item to search for.
/// The index of the first item equal to . -1 if no such item exists in the list.
public static int FirstIndexOf(IList list, T item)
{
return FirstIndexOf(list, item, EqualityComparer.Default);
}
///
/// Finds the index of the first item in a list equal to a given item. A passed
/// IEqualityComparer is used to determine equality.
///
/// The list to search.
/// The item to search for.
/// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called.
/// The index of the first item equal to . -1 if no such item exists in the list.
public static int FirstIndexOf(IList list, T item, IEqualityComparer equalityComparer)
{
if (list == null)
throw new ArgumentNullException("list");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
int index = 0;
foreach (T x in list) {
if (equalityComparer.Equals(x, item)) {
return index;
}
++index;
}
// didn't find any item that matches.
return -1;
}
///
/// Finds the index of the last item in a list equal to a given item.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// The list to search.
/// The item to search for.
/// The index of the last item equal to . -1 if no such item exists in the list.
public static int LastIndexOf(IList list, T item)
{
return LastIndexOf(list, item, EqualityComparer.Default);
}
///
/// Finds the index of the last item in a list equal to a given item. A passed
/// IEqualityComparer is used to determine equality.
///
/// The list to search.
/// The item to search for.
/// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called.
/// The index of the last item equal to . -1 if no such item exists in the list.
public static int LastIndexOf(IList list, T item, IEqualityComparer equalityComparer)
{
if (list == null)
throw new ArgumentNullException("list");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
for (int index = list.Count - 1; index >= 0; --index) {
if (equalityComparer.Equals(list[index], item)) {
return index;
}
}
// didn't find any item that matches.
return -1;
}
///
/// Enumerates the indices of all the items in a list equal to a given item.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// The list to search.
/// The item to search for.
/// An IEnumerable<T> that enumerates the indices of items equal to .
public static IEnumerable IndicesOf(IList list, T item)
{
return IndicesOf(list, item, EqualityComparer.Default);
}
///
/// Enumerates the indices of all the items in a list equal to a given item. A passed
/// IEqualityComparer is used to determine equality.
///
/// The list to search.
/// The item to search for.
/// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called.
/// An IEnumerable<T> that enumerates the indices of items equal to .
public static IEnumerable IndicesOf(IList list, T item, IEqualityComparer equalityComparer)
{
if (list == null)
throw new ArgumentNullException("list");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
int index = 0;
foreach (T x in list) {
if (equalityComparer.Equals(x, item)) {
yield return index;
}
++index;
}
}
///
/// Finds the index of the first item in a list equal to one of several given items.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// The list to search.
/// The items to search for.
/// The index of the first item equal to any of the items in the collection .
/// -1 if no such item exists in the list.
public static int FirstIndexOfMany(IList list, IEnumerable itemsToLookFor)
{
return FirstIndexOfMany(list, itemsToLookFor, EqualityComparer.Default);
}
///
/// Finds the index of the first item in a list equal to one of several given items. A passed
/// IEqualityComparer is used to determine equality.
///
/// The list to search.
/// The items to search for.
/// The IEqualityComparer<T> used to compare items for equality.
/// Only the Equals and GetHashCode methods will be called.
/// The index of the first item equal to any of the items in the collection .
/// -1 if no such item exists in the list.
public static int FirstIndexOfMany(IList list, IEnumerable itemsToLookFor, IEqualityComparer equalityComparer)
{
if (list == null)
throw new ArgumentNullException("list");
if (itemsToLookFor == null)
throw new ArgumentNullException("itemsToLookFor");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
// Create a set of the items we are looking for, for efficient lookup.
Set setToLookFor = new Set(itemsToLookFor, equalityComparer);
// Scan the list for the items.
int index = 0;
foreach (T x in list) {
if (setToLookFor.Contains(x)) {
return index;
}
++index;
}
// didn't find any item that matches.
return -1;
}
///
/// Finds the index of the first item in a list "equal" to one of several given items. The passed
/// BinaryPredicate is used to determine if two items are "equal".
///
/// Since an arbitrary BinaryPredicate is passed to this function, what is being removed need not be true equality. This methods finds
/// first item X which satisfies BinaryPredicate(X,Y), where Y is one of the items in
/// The list to search.
/// The items to search for.
/// The BinaryPredicate used to compare items for "equality".
/// The index of the first item "equal" to any of the items in the collection , using
/// as the test for equality.
/// -1 if no such item exists in the list.
public static int FirstIndexOfMany(IList list, IEnumerable itemsToLookFor, BinaryPredicate predicate)
{
if (list == null)
throw new ArgumentNullException("list");
if (itemsToLookFor == null)
throw new ArgumentNullException("itemsToLookFor");
if (predicate == null)
throw new ArgumentNullException("predicate");
// Scan the list for the items.
int index = 0;
foreach (T x in list) {
foreach (T y in itemsToLookFor) {
if (predicate(x, y)) {
return index;
}
}
++index;
}
// didn't find any item that matches.
return -1;
}
///
/// Finds the index of the last item in a list equal to one of several given items.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// The list to search.
/// The items to search for.
/// The index of the last item equal to any of the items in the collection .
/// -1 if no such item exists in the list.
public static int LastIndexOfMany(IList list, IEnumerable itemsToLookFor)
{
return LastIndexOfMany(list, itemsToLookFor, EqualityComparer.Default);
}
///
/// Finds the index of the last item in a list equal to one of several given items. A passed
/// IEqualityComparer is used to determine equality.
///
/// The list to search.
/// The items to search for.
/// The IEqualityComparer<T> used to compare items for equality.
/// The index of the last item equal to any of the items in the collection .
/// -1 if no such item exists in the list.
public static int LastIndexOfMany(IList list, IEnumerable itemsToLookFor, IEqualityComparer equalityComparer)
{
if (list == null)
throw new ArgumentNullException("list");
if (itemsToLookFor == null)
throw new ArgumentNullException("itemsToLookFor");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
// Create a set of the items we are looking for, for efficient lookup.
Set setToLookFor = new Set(itemsToLookFor, equalityComparer);
// Scan the list
for (int index = list.Count - 1; index >= 0; --index) {
if (setToLookFor.Contains(list[index])) {
return index;
}
}
// didn't find any item that matches.
return -1;
}
///
/// Finds the index of the last item in a list "equal" to one of several given items. The passed
/// BinaryPredicate is used to determine if two items are "equal".
///
/// Since an arbitrary BinaryPredicate is passed to this function, what is being removed need not be true equality. This methods finds
/// last item X which satisfies BinaryPredicate(X,Y), where Y is one of the items in
/// The list to search.
/// The items to search for.
/// The BinaryPredicate used to compare items for "equality".
/// The index of the last item "equal" to any of the items in the collection , using
/// as the test for equality.
/// -1 if no such item exists in the list.
public static int LastIndexOfMany(IList list, IEnumerable itemsToLookFor, BinaryPredicate predicate)
{
if (list == null)
throw new ArgumentNullException("list");
if (itemsToLookFor == null)
throw new ArgumentNullException("itemsToLookFor");
if (predicate == null)
throw new ArgumentNullException("predicate");
// Scan the list
for (int index = list.Count - 1; index >= 0; --index) {
foreach (T y in itemsToLookFor) {
if (predicate(list[index], y)) {
return index;
}
}
}
// didn't find any item that matches.
return -1;
}
///
/// Enumerates the indices of all the items in a list equal to one of several given items.
///
/// The default sense of equality for T is used, as defined by T's
/// implementation of IComparable<T>.Equals or object.Equals.
/// The list to search.
/// A collection of items to search for.
/// An IEnumerable<T> that enumerates the indices of items equal to
/// any of the items in the collection .
public static IEnumerable IndicesOfMany(IList list, IEnumerable itemsToLookFor)
{
return IndicesOfMany(list, itemsToLookFor, EqualityComparer.Default);
}
///
/// Enumerates the indices of all the items in a list equal to one of several given items. A passed
/// IEqualityComparer is used to determine equality.
///
/// The list to search.
/// A collection of items to search for.
/// The IEqualityComparer<T> used to compare items for equality.
/// An IEnumerable<T> that enumerates the indices of items equal to
/// any of the items in the collection