Searching a Generic List

I have no problem making and using a generic list. They work fantastic. However, I'm having a major problem being able to search them in the manner I want to.

If I make a generic list to store some objects of some type, I want to be able to search the list not by passing it an instance of the object, (such as List.Contains(MyObj), or List.IndexOf(MyObj)), but rather by passing it the value of one of the properties of my object. For example, I cannot get this to work.

Dim MyList as System.Generic.List(Of MyObject)

Public Class MyObject

Public property Name

Public Property Date

End Class

... create some instances of my object and add them to list...

Query my list like so...

Index = MyList.IndexOf("John")

or

blnFound = MyList.Contains("John")

Once you create a list to accept a certain type of object, it appears impossible to pass a generic list anything but an instance of that type, and I don't have this restriction when using an arraylist. I can override Equals and make it search the arraylist by anything I want. Is this not possible with generic lists




Answer this question

Searching a Generic List

  • Andy C

    Obscurity wrote:
    Once you create a list to accept a certain type of object, it appears impossible to pass a generic list anything but an instance of that type, and I don't have this restriction when using an arraylist. I can override Equals and make it search the arraylist by anything I want. Is this not possible with generic lists

    I haven't thought about this very hard but it seems to me the reason IndexOf and Contains only take parameters of type T is that they use the default EqualityComparer for T which compares two items of type T.

    One alternative would be to use the Predicate based methods: Find, FindAll, FindIndex, FindLast, FindLastIndex. The challenge is building a Predicate that finds what you want in a sufficiently flexible fashion.

    One approach would be to build a small class to hold the desired value and to provide a Predicate that uses the desired value to calculate the return value. For example...


    class FindTarget
    {
    public FindTarget(string value)
    {
    _target = value;
    }

    private string _target;

    public bool Match(MyObject t)
    {
    return _target.Equals(t.Name);
    }
    }

    The class constructor can be used to specify the value you want to look for and the function Match can be used as the Predicate. For example...


    FindTarget target = new FindTarget("John");

    Index = MyList.FindIndex(target.Match);


  • gleason78

    I agree that there would be some overhead to it, however without some testing it is impossible to know how much in your situation and and if it would be more than you’d be willing to accept.

  • starz

    I thought about a dictionary, but the reason I didn't go with that is that I need my elements to stay in the order that they were added to the collection. A dictionary doesn't maintain any order, it just throws the object references into buckets which are accessed quickly by the key.

    Using both a dictionary and a list would work, however, it would then be an issue of overhead. This piece of code could be accessed in extremely fast succession, so I need to do as much as possible with as little code as possible.

    Basically, this code is going to run on a spreadsheet-like control every time the user changes a cell. If they do a fill down on a cell, it would need to run potentially thousands of times as quickly as possible to not delay the visual interface.



  • BTognietti

    You're right, this might work as well.  I am currently doing something similar in that I am creating a new empty instance of MyObject, and then setting the property I want to find on it, and passing it to the list like so...

      List.IndexOf(MyObject)

    Each instance of MyObject implements IEquatable(Of MyObject)

    In that code I do something like ...

       if me.Name = MyObject.Name then return True else return False

    This effectively achieves the same thing as a predicate.  However, I'm still not giving up hope on a slightly more elegant solution.  Perhaps overriding something or implementing some interface or something   You mentioned the EqualityComparer interface.  I wonder if it's possible to make a List<of T> use something other than the default.

     

     



  • cepe

    One alternative would be to use a Dictionary class, either as a replacement to your list, a second method of list indexing, or built into an overall wrapper class that did everything you want.

    With a Dictionary class you could specify a name for each element when you add an element to the Dictionary. The obvious upshot of this is that you can query for a specific item provided you know the name you gave it when you added it, the down side is that it doesn't support the same kind of array style logic that you'd get with just a List.

    One way around this though would be to use both a Dictionary and a List, when you add something to the list you also add it to the dictionary with the desired name, that way you have the best of both worlds.

    Could this work for you



  • kicks_joy_darkness

    I thank you for responding so quickly, but before I mark you as Answered, I want to make sure that Generics is unable to do this. Hopefully someone vastly skilled in Generics will trip over this question in their meanderings through MSDN.

  • Searching a Generic List