Which generic?

  • Thread starter Thread starter wsmckenz
  • Start date Start date
W

wsmckenz

I want a collection of things, Columns in this case, which are indexed
by name (which would imply a Dictionary<string, Column>) but are also
in a guaranteed order (which would imply List<Column>). I want to be
able to write:

m_columns;

// or

m_columns["FRED"];

and have both work.

i also want

foreach(Column col in m_columns)
{
}

To enumerate in the order that the columns were appended to the list.

Is there a generic that serves the purpose or do I have to write my
own?
 
wsmckenz said:
I want a collection of things, Columns in this case, which are indexed
by name (which would imply a Dictionary<string, Column>) but are also
in a guaranteed order (which would imply List<Column>). I want to be
able to write:

m_columns;

// or

m_columns["FRED"];

and have both work.

i also want

foreach(Column col in m_columns)
{
}

To enumerate in the order that the columns were appended to the list.

Is there a generic that serves the purpose or do I have to write my
own?


Either SortedList<> or SortedDictionary<> should do.
 
wsmckenz said:
I want a collection of things, Columns in this case, which are indexed
by name (which would imply a Dictionary<string, Column>) but are also
in a guaranteed order (which would imply List<Column>).   I want to be
able to write:
m_columns;


// or
m_columns["FRED"];

and have both work.

i also want
foreach(Column col in m_columns)
{
}
To enumerate in the order that the columns were appended to the list.
Is there a generic that serves the purpose or do I have to write my
own?

Either SortedList<> or SortedDictionary<> should do.


I dont think that works. For instance, the following code
using System;
using System.Collections.Generic;

public struct Column
{
public string name;
public string description;
public int type;
public Column(string nameIn, string descIn, int typeIn)
{
name = nameIn;
description = descIn;
type = typeIn;
}
}

public class foo
{
static Column[] myCols =
{
new Column("ABC", "abc", 2),
new Column("DEF", "def", 3),
new Column("XYZ", "xyz", 1),
new Column("QRS", "qrs", 2)
};

static void Main()
{
SortedList<string, Column> cols = new SortedList<string,
Column>();
foreach(Column col in myCols)
{
cols.Add(col.name, col);
}

foreach(string name in cols.Keys)
{
System.Console.Out.WriteLine(cols[name].name);
}

System.Console.Out.WriteLine("====================");

for (int i=0; i<cols.Count; ++i)
{
System.Console.Out.WriteLine(cols.Values.name);
}
}
}


Produces the following output:

C:\work>generic
ABC
DEF
QRS
XYZ
====================
ABC
DEF
QRS
XYZ

This is not what I want. I want the items to remain in the list in
the order i put them there, but also be accessible by name.
 
wsmckenz said:
wsmckenz said:
I want a collection of things, Columns in this case, which are indexed
by name (which would imply a Dictionary<string, Column>) but are also
in a guaranteed order (which would imply List<Column>). I want to be
able to write:
m_columns;
// or
m_columns["FRED"];
and have both work.
i also want
foreach(Column col in m_columns)
{
}
To enumerate in the order that the columns were appended to the list.
Is there a generic that serves the purpose or do I have to write my
own?

Either SortedList<> or SortedDictionary<> should do.


I dont think that works. For instance, the following code
using System;
using System.Collections.Generic;

public struct Column
{
public string name;
public string description;
public int type;
public Column(string nameIn, string descIn, int typeIn)
{
name = nameIn;
description = descIn;
type = typeIn;
}
}

public class foo
{
static Column[] myCols =
{
new Column("ABC", "abc", 2),
new Column("DEF", "def", 3),
new Column("XYZ", "xyz", 1),
new Column("QRS", "qrs", 2)
};

static void Main()
{
SortedList<string, Column> cols = new SortedList<string,
Column>();
foreach(Column col in myCols)
{
cols.Add(col.name, col);
}

foreach(string name in cols.Keys)
{
System.Console.Out.WriteLine(cols[name].name);
}

System.Console.Out.WriteLine("====================");

for (int i=0; i<cols.Count; ++i)
{
System.Console.Out.WriteLine(cols.Values.name);
}
}
}


Produces the following output:

C:\work>generic
ABC
DEF
QRS
XYZ
====================
ABC
DEF
QRS
XYZ

This is not what I want. I want the items to remain in the list in
the order i put them there, but also be accessible by name.


Ah, I see. In that case, you indeed have to roll your own. This shouldn't be
hard: you want to aggregate a List<Column> and a Dictionary<string, Column>.
The class basically implements IList<Column> and IDictionary<string, Column>
and delegates as appropriate:

class ColumnCollection : IList<Column>, IDictionary<string, Column> {
private readonly IList<Column> columnsByIndex = new List<Column>();
private readonly IDictionary<string, Column> columnsByName = new
Dictionary<string, Column>();

#region IList<Column> Members

public int IndexOf(Column item) {
return columnsByIndex.IndexOf(item);
}

public void Insert(int index, Column item) {
columnsByIndex.Insert(index, item);
columnsByName.Add(item.name, item);
}

...

and so on. Aside from being a fair bit of typing, there's little difficulty
involved. You'll have to decide at a few points whether you want a
list-centric view or a dictionary-centric view of your class (such as when
you're enumerating).
 
wsmckenz said:
wsmckenz wrote:
I want a collection of things, Columns in this case, which are indexed
by name (which would imply a Dictionary<string, Column>) but are also
in a guaranteed order (which would imply List<Column>).   I want to be
able to write:
m_columns;
// or
m_columns["FRED"];
and have both work.
i also want
foreach(Column col in m_columns)
{
}
To enumerate in the order that the columns were appended to the list.
Is there a generic that serves the purpose or do I have to write my
own?
Either SortedList<> or SortedDictionary<> should do.

I dont think that works.  For instance, the following code
using System;
using System.Collections.Generic;
public struct Column
{
  public string name;
  public string description;
  public int type;
  public Column(string nameIn, string descIn, int typeIn)
  {
    name = nameIn;
    description = descIn;
    type = typeIn;
  }
}
public class foo
{
  static Column[] myCols =
  {
    new Column("ABC", "abc", 2),
    new Column("DEF", "def", 3),
    new Column("XYZ", "xyz", 1),
    new Column("QRS", "qrs", 2)
  };
  static void Main()
  {
    SortedList<string, Column> cols = new SortedList<string,
Column>();
    foreach(Column col in myCols)
    {
      cols.Add(col.name, col);
    }
    foreach(string name in cols.Keys)
    {
      System.Console.Out.WriteLine(cols[name].name);
    }
    System.Console.Out.WriteLine("====================");
    for (int i=0; i<cols.Count; ++i)
    {
      System.Console.Out.WriteLine(cols.Values.name);
    }
  }
}

Produces the following output:

This is not what I want.  I want the items to remain in the list in
the order i put them there, but also be accessible by name.

Ah, I see. In that case, you indeed have to roll your own. This shouldn't be
hard: you want to aggregate a List<Column> and a Dictionary<string, Column>.
The class basically implements IList<Column> and IDictionary<string, Column>
and delegates as appropriate:

   class ColumnCollection : IList<Column>, IDictionary<string, Column>{
     private readonly IList<Column> columnsByIndex = new List<Column>();
     private readonly IDictionary<string, Column> columnsByName = new
Dictionary<string, Column>();

     #region IList<Column> Members

     public int IndexOf(Column item) {
       return columnsByIndex.IndexOf(item);
     }

     public void Insert(int index, Column item) {
       columnsByIndex.Insert(index, item);
       columnsByName.Add(item.name, item);
     }

     ...

and so on. Aside from being a fair bit of typing, there's little difficulty
involved. You'll have to decide at a few points whether you want a
list-centric view or a dictionary-centric view of your class (such as when
you're enumerating).


thanks, thats more or less what i thought. Just didn't want to do all
that typing if I didn't have to.

~Bill
 
Hi Jeroem,

What about just extending List<T>

class Column Collection : List<Column>
{
Column this[string someName]
{
get
{
foreach(Column col in this)
{
if (string.Equals(col.ColumnName, someName,
StringComparison.Ordinal))
return col;
}

return null;
}
}
}



wsmckenz said:
wsmckenz wrote:
I want a collection of things, Columns in this case, which are indexed
by name (which would imply a Dictionary<string, Column>) but are also
in a guaranteed order (which would imply List<Column>).   I want tobe
able to write:
m_columns;
// or
m_columns["FRED"];
and have both work.
i also want
foreach(Column col in m_columns)
{
}
To enumerate in the order that the columns were appended to the list.
Is there a generic that serves the purpose or do I have to write my
own?
Either SortedList<> or SortedDictionary<> should do.

I dont think that works.  For instance, the following code
using System;
using System.Collections.Generic;
public struct Column
{
  public string name;
  public string description;
  public int type;
  public Column(string nameIn, string descIn, int typeIn)
  {
    name = nameIn;
    description = descIn;
    type = typeIn;
  }
}
public class foo
{
  static Column[] myCols =
  {
    new Column("ABC", "abc", 2),
    new Column("DEF", "def", 3),
    new Column("XYZ", "xyz", 1),
    new Column("QRS", "qrs", 2)
  };
  static void Main()
  {
    SortedList<string, Column> cols = new SortedList<string,
Column>();
    foreach(Column col in myCols)
    {
      cols.Add(col.name, col);
    }
    foreach(string name in cols.Keys)
    {
      System.Console.Out.WriteLine(cols[name].name);
    }
    System.Console.Out.WriteLine("====================");
    for (int i=0; i<cols.Count; ++i)
    {
      System.Console.Out.WriteLine(cols.Values.name);
    }
  }
}

Produces the following output:

This is not what I want.  I want the items to remain in the list in
the order i put them there, but also be accessible by name.

Ah, I see. In that case, you indeed have to roll your own. This shouldn'tbe
hard: you want to aggregate a List<Column> and a Dictionary<string, Column>.
The class basically implements IList<Column> and IDictionary<string, Column>
and delegates as appropriate:

   class ColumnCollection : IList<Column>, IDictionary<string, Column> {
     private readonly IList<Column> columnsByIndex = new List<Column>();
     private readonly IDictionary<string, Column> columnsByName =new
Dictionary<string, Column>();

     #region IList<Column> Members

     public int IndexOf(Column item) {
       return columnsByIndex.IndexOf(item);
     }

     public void Insert(int index, Column item) {
       columnsByIndex.Insert(index, item);
       columnsByName.Add(item.name, item);
     }

     ...

and so on. Aside from being a fair bit of typing, there's little difficulty
involved. You'll have to decide at a few points whether you want a
list-centric view or a dictionary-centric view of your class (such as when
you're enumerating).
 
I want a collection of things, Columns in this case, which are indexed
by name (which would imply a Dictionary<string, Column>) but are also
in a guaranteed order (which would imply List<Column>).   I want to be
able to write:

m_columns;

// or

m_columns["FRED"];

and have both work.

i also want

foreach(Column col in m_columns)
{

}

To enumerate in the order that the columns were appended to the list.

Is there a generic that serves the purpose or do I have to write my
own?


A generic, no. There is
System.Collections.Specialized.OrderedDictionary, though - if you
don't mind the performance hit, you might want to just wrap it in
generic IList/IDictionary, and use that (or just do the casts as
needed).
 
What about just extending List<T>

class Column Collection : List<Column>
{
Column this[string someName]
{
get
{
foreach(Column col in this)
{
if (string.Equals(col.ColumnName, someName,
StringComparison.Ordinal))
return col;
}

return null;
}
}
}
This is a workable solution, however:

- It doesn't implement IDictionary<string, Column>, so it can't be passed to
methods that operate on dictionaries, even though this container logically
is one. Of course, this is a relatively minor issue if you never plan on
doing that anyway.

- It doesn't scale for many columns or for many lookups, since lookup takes
time linear to the number of columns (rather than near-constant time if a
dictionary is used). This may not be an issue if you only ever plan to use
it for a small number of columns, but then you should clearly document that
this is how the class should be used.

- Because it extends List rather than aggregating it, it can't maintain
additional invariants. In particular, it's possible to add a column with the
same name twice. The lookup by name will only ever return one of these
elements. This is unlikely to be what you want.
 
Back
Top