views:

1085

answers:

5

I'm working on a problem in C# 2.0/.NET 2.0 where I have a Sortedlist and want to search all the "values" (not the "keys") of this SortedList for a certain substring and count up how many occurrences there are.

This is what I'm trying to do:

{
   Sortedlist<string,string> mySortedList;
   // some code that instantiates mySortedList and populates it with data
   List<string> myValues = mySortedList.Values;  // <== does not work
   int namesFound = myValues.FindAll(ByName(someName)).Count;
}

Naturally, this doesn't work because mySortedList.Values returns an IList, while "myValues" is a List. I tried "casting" the IList so that it would be accepted by myValues, but it doesn't seem to work.

Of course, I can loop over mySortedList.Values in a "foreach" loop, but I don't really want to do this.

Anyone have any suggestions?

EDIT-1: Ok, well it looks like there isn't a native way to do this easily. I had assumed that I was just missing something, but apparently I'm not. So I guess I'm just going to do a "foreach" over the IList.

Thanks for the feedback everyone! I voted everyone up 1 because I thought all the feedback was good. Thanks again! :-)

EDIT-2: Looks like CMS has the answer I was looking for. The only caveat with this (as Qwertie pointed out) is that there is a potential performance penalty since it involves copying all the values to another List and then searching that list start-to-finish. So for short lists, this answer is effective. Longer lists? well that's up to you to decide...

+1  A: 

Too bad you're in .Net 2.0. This is exactly what LINQ is for. ;)

You can't really do this, since FindAll() is a member of List. You could create a new List on the mySortedList.Values, but it seems to be a waste, since a new object and underlying array needs to be allocated just to call a function.

I'd just write a utility function in some class for lists called FindAll() and then pass your IList and delegate.

codekaizen
+1  A: 
 static void Main(string[] args)
 {
  string someName = "two";
  SortedList<string, string> mySortedList = new SortedList<string,string>()
  {
   {"key1", "This is key one"},
   {"key2", "This is key two"},
   {"key3", "This is key three"},
  };

  int namesFound = mySortedList.Values.Where(i => i.Contains(someName)).Count();
  Console.WriteLine(namesFound);
  Console.ReadKey();
 }

In Framework 2.0, maybe can do this:

 static void Main(string[] args)
 {
  string someName = "two";
  SortedList<string, string> mySortedList = new SortedList<string,string>()
  {
   {"key1", "This is key one"},
   {"key2", "This is key two"},
   {"key3", "This is key three"},
  };

  int namesFound = FindAll(mySortedList.Values, someName).Count ;
  Console.WriteLine(namesFound);
  Console.ReadKey();
 }
 public static IList<String> FindAll(IList<String> items, string item)
 {
  List<String> result = new List<string>();
  foreach (String s in items)
  {
   if (s.Contains(item))
   {
    result.Add(s);
   }
  }
  return result;
 }

But that is what you really did not want to do.

Bill
Poster said they were using .Net 2.0
Mitch Wheat
OOps. missed that one. Sorry, poster.
Bill
I think you meant "public static List<string>" (rather than returning another IList<string>), but I knew what you meant. Nice code. I'm giving the solution checkmark to you... :-)
Pretzel
Hmmm, looks like CMS came up with the answer that I was missing so I'm awarding him the "official answer" checkmark instead. Thanks again for the help! :)
Pretzel
+1  A: 

Have you tried SortedList's GetValueList yet? This'll give you back an IList of values.

dirkgently
Unfortunately, 'GetValueList; is not a property of generic version of SortedList<TKey, TValue>.
Ray Vega
+1  A: 

You can't cast the Values property to List<string> because it is not a List<string>--it is a Dictionary<TKey, TValue>.ValueCollection.

But if you use LinqBridge (for .NET Framework 2.0 with C# 3.0), this problem is easily solved with LINQ as follows:

SortedList<string, string> m = ...;
int namesFound = m.Values.Where(v => v.Contains("substring")).Count();

(if you're still on C# 2.0, you can use Poor-man's LINQ instead, with slightly more work)

Qwertie
+2  A: 

Since the IList Interface implements IEnumerable, you can actually get a List<T> of values using the List<T> (IEnumerable) Constructor:

List<string> myValues = new List<string>(mySortedList.Values);
CMS
Ahh, thank you! I *was* overlooking something. I hadn't realized that IList implements IEnumerable. Thinking about it now, it makes complete sense, but it didn't even occur to me at the time. This is the answer! Thanks CMS! :-)
Pretzel
With this solution, the performance implications of copying the entire list should be kept in mind.
Qwertie
Seems to me like the possibility of this being a performance issue is not worth the small refactoring to another method. Creating a new, potentially large object just to call a straightforward method on it? Bit of a smell there...
codekaizen
Qwertie: Good point. I did indeed realize the implications of copying the whole list, but the typical scenario has only ~10 to 50 values. (worst-case is ~400 values) -- A relatively trivial amount of work at runtime and my code stays simple and readable. A fair trade-off, I think. Thanks again!
Pretzel