Re: foreach with options?

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance





Steve Barnett wrote:
I have a collection which I need to navigate through in several different
ways. Is there any way to structure my class such that I can have several
different iterators?

Yes, give it properties returning different views:

class MyClass: ICollection<Foo> {
ICollection<Foo> AllNodes { get { return this; } }
ICollection<Foo> BlueNodes { get { return /* something else */; } }
}

Which would allow you to rewrite to:

Ideally, I'd like to do something like:

// Iterate whole collection
foreach (object in collection)

foreach( object o in myClassIntances )

or

foreach( object o in myClassIntances.AllNodes )


// Iterate over all objects with the "open" flag set
foreach (object in collection.opennodes)

foreach ( object o in myClassInstance.OpenNodes )


// Iterate over all blue objects
foreach (object in collection.bluenodes)

My general suggestion is to write a "filter-dictionary", so you can simply:

Dictionary<Foo,Bar> dict = ...;
foreach ( Foo foo in
new FilterCollection(
dict.Keys,
delegate(Foo x) { return x.isBlue(); })
...;

If you often do the same transformation, give it a name:

public static ICollection<Foo> BlueNodes(ICollection<Foo> nodes) {
return new FilterCollection(
node,
delegate(Foo x) { return x.isBlue(); });
}

and if your "masert-dictionary" collection should supply callers with
different views, as suggested at the top of this post:

public class MyDict: IDictionary<Foo, Bar> {
...
ICollection Foo BlueKeys { get { return BlueNodes(Keys); } }
}

Here as an implementation suggestion:

/// <summary>
/// Filters an ICollection to only contains the values satisfying a
predicate
/// </summary>
public class FilterCollection<T> : ICollection<T>
{
public ICollection<T> Parent;
public delegate bool FilterFunction(T t);
public FilterFunction Filter;
public FilterCollection(ICollection<T> parent, FilterFunction
filter)
{
this.Parent = parent;
this.Filter = filter;
}
public int Count
{
get
{
int count = 0;
foreach (T t in Parent)
if (Filter(t))
++count;
return count;
}
}
public bool IsReadOnly { get { return Parent.IsReadOnly; } }
public void Add(T item)
{
if (!Filter(item))
throw new ArgumentException(string.Format("Filter
rejected {0}", item));
else
Parent.Add(item);
}
public void Clear() { throw new Immutable(this); }
public bool Contains(T item) { return Parent.Contains(item) &&
Filter(item); }
public void CopyTo(T[] array, int arrayIndex)
{
foreach (T t in this)
array[arrayIndex++] = t;
}
public bool Remove(T item) { return Filter(item) &&
Parent.Remove(item); }
public IEnumerator<T> GetEnumerator()
{
foreach (T t in Parent)
if (Filter(t))
yield return t;
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}

/// <summary>
/// Filters an IDictionary to only contain the entries satisfying a
predicate
/// </summary>
public class FilterDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
public IDictionary<TKey, TValue> Parent;
public delegate bool FilterFunction(TKey key, TValue value);
public FilterFunction Filter;
public FilterDictionary(IDictionary<TKey, TValue> parent,
FilterFunction filter)
{
this.Parent = parent;
this.Filter = filter;
}
public virtual TValue this[TKey key]
{
get
{
TValue val = Parent[key];
if (Filter(key, val))
return val;
else
throw new KeyNotFoundException(string.Format("Not
accepted by filter: {0}->{1}", key, val));
}
set
{
if (Filter(key, value))
Parent[key] = value;
else
throw new KeyNotFoundException(string.Format("Not
accepted by filter: {0}", key));
}
}
protected bool keyFilter(TKey key) { return Filter(key,
Parent[key]); }
public ICollection<TKey> Keys { get { return new
FilterCollection<TKey>(Parent.Keys, keyFilter); } }
public ICollection<TValue> Values { get { throw new
NotImplementedException("Pending need for implementation"); } }
public int Count { get { return Keys.Count; } }
public bool IsReadOnly { get { return Parent.IsReadOnly; } }
public bool ContainsKey(TKey key)
{
TValue val;
return TryGetValue(key, out val);
}
public void Add(TKey key, TValue value)
{
if (!Filter(key, value))
throw new ArgumentException(string.Format("Filter
rejected {0}=>{1}", key, value));
Parent.Add(key, value);
}
public bool Remove(TKey key)
{
TValue val;
return TryGetValue(key, out val) && Parent.Remove(key);
}
public bool TryGetValue(TKey key, out TValue value) { return
Parent.TryGetValue(key, out value) && Filter(key, value); }
public void Add(TKey item) { throw new
NotSupportedException("Cannot add key-only"); }
public void Clear() { throw new
NotSupportedException(string.Format("Cannot clear {0}", GetType())); }
public bool Contains(TKey item) { return ContainsKey(item); }
public void CopyTo(TKey[] array, int arrayIndex) {
this.Keys.CopyTo(array, arrayIndex); }
public IEnumerator<TKey> GetEnumerator()
{ return Keys.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { throw new
NotImplementedException(); }
public DictionaryKeyValuePairCollection<TKey, TValue>
AsCollection(IEqualityComparer<TValue> comparer)
{ return new DictionaryKeyValuePairCollection<TKey,
TValue>(this, comparer); }
public DictionaryKeyValuePairCollection<TKey, TValue> AsCollection()
{ return new DictionaryKeyValuePairCollection<TKey, TValue>(this); }
IEnumerator<KeyValuePair<TKey, TValue>>
IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{ return AsCollection().GetEnumerator(); }
public bool Remove(KeyValuePair<TKey, TValue> kvp)
{ return AsCollection().Remove(kvp); }
public bool Contains(KeyValuePair<TKey, TValue> kvp)
{ return AsCollection().Contains(kvp); }
public void Add(KeyValuePair<TKey, TValue> kvp)
{ AsCollection().Add(kvp); }
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{ AsCollection().CopyTo(array, index); }
}
}
--
Helge Jensen
mailto:helge.jensen@xxxxxxx
sip:helge.jensen@xxxxxxx
-=> Sebastian cover-music: http://ungdomshus.nu <=-
.



Relevant Pages

  • Re: Filtering strongly-typed datasets
    ... foreach (DataRowView row in dv) {//... ... SQL-esque query or somesuch? ... I have wanted filter a dataset I have obtained from a generic method and pull out the rows I am actually interested in. ...
    (microsoft.public.dotnet.framework)
  • Re: foreach broken in my script
    ... my @Filter =; ... my @DNS =; ... foreach my $vm { ...
    (perl.beginners)
  • RE: directory operations
    ... If you did want to filter based on a regular expression, you can always do it the long way: ... opendir|| die; ... foreach my $file{ ...
    (perl.beginners)
  • Dataset performance
    ... For example the first thing I do is to filter that table with so many ... foreach(DataRow dr in ds.TableWithManyData.Select(FILTER)){ ... Maxi Menasches ...
    (microsoft.public.dotnet.framework.adonet)