Searching List of objects

VBAHole22

Contributor
Joined
Oct 21, 2003
Messages
432
Location
VA
I have an object called Node that has 3 properties. I create a List generic to hold a bunch of these node objects.

Then I want to loop over a different collection of nodes and find out if they exist in the first collection mentioned above. How can I go about that? I know there is an IndexOf method on the List object but I think that is to find the same instance of a node (which in my case would never return true).

I see that there are Find methods that take predicates but I am not testing to see if the node matches some pattern, I just want to see if my node is in the List collection.

I would imagine that i have to override Equals or Compare because I figure there is no way .NET is going to know which of the 3 properties I want to compare. It turns out that I only want to compare one of the props and not all.

Any code suggestions?
 
I beleive that List<T> has a Contains method. I would use that. The trick is to get the Contains method to work for your Node class the exact way you want it to. To get that to work, you need to implement the IEquatable interface in your Node class.

The basic gist is that Contains wants to check an Equals method so it requests an EqualityComparer from the object it is checking. For your object to return one, it needs to implement IEquatable. If you don't, your custom object will return a default EqualityComparer which probably won't do what you want it to.

And some references for the above stuff I talked about...
http://msdn2.microsoft.com/en-us/library/bhkz42b3.aspx
http://msdn2.microsoft.com/en-us/library/ms224763.aspx
http://msdn2.microsoft.com/en-us/library/ms131187.aspx
 
Cool. Va keepin' it real.

I am going to try what you suggest but in the meantime I think this is working:

Code:
  public override bool Equals(object obj)
        {
            if (obj == null) return false; //1if the obj passed in is null it can't be equal to anything
            if (Object.ReferenceEquals(this, obj)) return true;//2if the object passed in is the same instance then its equal
            if (this.GetType() != obj.GetType()) return false; //3if the objects aren't the same type they can't be equal
            Node n = obj as Node;
            if (n == null) return false;//if the cast failed it ain't equal this might be the same as 3
            if (nodeID.Equals(n.nodeID)) return true;
            return false;
        }

        public override int GetHashCode()
        {
            return nodeID.GetHashCode();
        }
 
Overriding Equals works, and IEquitable works. Predicates can work too.

C#:
// Create a collection of objects for the example
List<Control> controls = new List<Control>();

public void PrepareExample() {
    // Populate list
    controls.AddRange(new Button[] {new Button(), new Button()});
    // Set one of the objects so that it will match our search criteria.
    controls[1].Name = "ThisIsTheButtonYouWant";
}

public void PredicateExample() {
    // Create an object for the example that defines our search citeria.
    Control CompareAgainst = new Button();
    CompareAgainst.Name = "ThisIsTheButtonYouWant";

    // This is the important code
    Control ControlIFound = controls.Find(
        delegate(Control c)
        {
            return c.Name == CompareAgainst.Name;
        }
    );

    MessageBox.Show(ControlIFound.Name);
}
That looks like a hefty chunk of code to determine whether a list has a certain object or not, but most of that code actually sets up the example. The heart of the code, which is the only code you will actually need to write in your program, is pretty short:
C#:
    Control ControlIFound = controls.Find(
        delegate(Control c)
        {
            return c.Name == CompareAgainst.Name;
        }
    );
More or less the same amount of code as would be written to override Equals or implement IEquitable. Like so:
C#:
// We will have a variable we want to compare to, for example:
Node checkAgainst;

// Create a bool that indicates whether the list has an equal node.
bool ItemIsInList = null != MyNodeList.Find(
    delegate(Node n) {
        return n == null ? 
            false :
            n.nodeID = checkAgainst.nodeID;
    }
};
Not recommending you fix your code that isn't broken, but it's always nice to have another way to do something, and Predicate<T> methods can do alot more than compare for equality.
 
That's actually a really sweet solution. It's great for one off generic collections that you can create on the fly and avoid all the overhead normally associated with creating a strongly typed collection. And the use of anonymous methods, while somewhat gratuitous, is very coder cool. I wouldn't recommend this for all situations but it seems like it would be perfect and elegant for quick one off collections.

Of course, if you're creating a collection that has any legs, you would probably be better off using one of the other, more generic, reusable solutions. But, man, that's hot! :cool: Thanks for posting that code up, marble_eater. I think that code just made my day.

References just for kicks:
http://msdn2.microsoft.com/en-us/library/bfcke1bz.aspx
http://msdn2.microsoft.com/en-us/library/0yw3tz5k.aspx
 
It just so happens that i do have a reason to fix up my code. And i greatly appreciate the predicate sample code - couldn't come across that on Google.

What happened was I wrote my Equals override to test if the name prop was the same between my test case and my collection. But then I got to a point where the comparison needed to be on the Name and Whatever props. So I can't re-write the Equals to have it work for both cases.

I don't have a solution yet for this. Seems like I need to pass in a param to tell my compare (or equals) method what exactly I want compared. The hack I came up with was to just create my own method for each kind of comparison so that the things you are comparing are actually in the name of the method so I have a method for CompareName and CompareWhatever and they behave differently. Being the experienced programmer that i am I can sniff out (and mastermind) a giant hack from around the corner. There has to be a better way but it does boil down to the fact that the comparing method needs to know what to compare.

Any ideas? Seems like this is what delegates are made for but i'm a little lost in that area.
And I greatly appreciate the support.
 
I was thinking about maybe using predicates for something similar I'm working on. http://www.xtremedotnettalk.com/showthread.php?t=97412

Is it possible to do something similar to this with predicates?Or is that to much? Any tutorials on them?That has me curious.So this is something new with 2005?

Or maybe not allow duplicates in added into the list in the first place and tally the totals to the first instance of that player object.

This is what the List class looks like...
Visual Basic:
Imports System
Imports System.IO
Imports System.Net
Imports System.Web



Public Class CPlayerList1
    Private m_Players As New Collection
    Private Player As CPlayer

    


    Public Sub New()

        'empty constructor

    End Sub
 


    Public Sub LoadPlayers()
        Try
            Const file As String = "pathtoftp"
            Dim client As New WebClient
            Dim ftpReq As FtpWebRequest = _
               WebRequest.Create(file)
            ftpReq.Method = WebRequestMethods.Ftp.DownloadFile
            ftpReq.KeepAlive = True
            ftpReq.UsePassive = True

            ftpReq.Credentials = New NetworkCredential( _
               "nunya", "business")
            Dim ftpResp As FtpWebResponse

            ftpResp = ftpReq.GetResponse
            Dim ftpRespStream As Stream
            Dim reader As StreamReader
            ftpRespStream = ftpResp.GetResponseStream
            Stream.Synchronized(ftpRespStream)
            reader = New IO.StreamReader(ftpRespStream, _
               System.Text.Encoding.ASCII)

            Do While StreamReader.Synchronized(reader).Read
                Dim myPlayer As String
                myPlayer = reader.ReadLine
                GetNames(myPlayer)
                GetKills(myPlayer)
                getDeaths(myPlayer)
                GetTk(myPlayer)
                GetSuicides(myPlayer)
                GetObjective(myPlayer)

            Loop
            reader.Close()



        Catch ex As Exception
            Dim mm As New System.Net.Mail.SmtpClient
            mm.Host = "smtp.charter.net"
            mm.Send("itower@chartermi.net", "itower@chartermi.net", "error", ex.Message)
        End Try
    End Sub
    Private Sub WritePlayers(ByVal Path As String)

    End Sub
   
    Private Sub GetObjective(ByVal myObjective As String)
        Dim myplayernames As String
        myplayernames = getPlayersObjective(myObjective)
        If myplayernames <> "" Then

            Player.Objective = getPlayersObjective(myObjective)
            m_Players.Add(Player) 'Add our player to our collection of players.####NOTE HERE####
        End If
    End Sub
    Private Function getPlayersObjective(ByVal myObjective As String)
        Dim reg As Regex = New Regex("Objective:+\s*\w*")
        Dim m As Match = reg.Match(myObjective)
        myObjective = m.Value.Replace("Objective: ", "")

        Return myObjective
    End Function

    Private Sub GetTk(ByVal tk As String)
        Dim myplayernames As String
        myplayernames = GetPlayerTeamKills(tk)
        If myplayernames <> "" Then

            Player.TeamKills = GetPlayerTeamKills(tk)
        

        End If
    End Sub
    Private Sub getDeaths(ByVal mydeaths As String)
        Dim myplayernames As String
        myplayernames = GetPlayerDeaths(mydeaths)
        If myplayernames <> "" Then

            Player.Deaths = GetPlayerDeaths(mydeaths)
            

        End If
    End Sub
    Private Sub GetNames(ByVal myNames As String)
        Dim myplayernames As String
        myplayernames = GetPlayerNames(myNames)
        If myplayernames <> "" Then
            Player = New CPlayer
            Player.Name = GetPlayerNames(myNames)
            

        End If
    End Sub
    Private Sub GetKills(ByVal amyKills As String)
        Dim myTempKills As String
        myTempKills = GetPlayerKills(amyKills)
        If myTempKills <> "" Then
            Player.Kills = GetPlayerKills(amyKills)
            

        End If
    End Sub
    Private Sub GetSuicides(ByVal mySuicides As String)
        Dim myTempKills As String
        myTempKills = GetPlayerSuicides(mySuicides)
        If myTempKills <> "" Then
            Player.Suicides = GetPlayerSuicides(mySuicides)
            

        End If
    End Sub
    Public Function GetPlayerSuicides(ByVal mySuicides As String)
        Dim reg As Regex = New Regex("Suicides:+\s*\w*")
        Dim m As Match = reg.Match(mySuicides)
        mySuicides = m.Value.Replace("Suicides: ", "")
        Return mySuicides
    End Function
    Public Function GetPlayerDeaths(ByVal myDEaths As String) As String
        Dim reg As Regex = New Regex("Deaths:+\s*\w*")
        Dim m As Match = reg.Match(myDEaths)
        myDEaths = m.Value.Replace("Deaths: ", "")

        Return myDEaths
    End Function
    Public Function GetPlayerNames(ByVal myPlayer As String) As String
        Dim reg As Regex = New Regex("\:+\s[a-zA-Z0-9<>?,.{}}\+_&=!@#$'%-|`*^:/;()~]+\s+\(u*")
        Dim m As Match = reg.Match(myPlayer)
        myPlayer = m.Value.TrimStart(": ").ToString 'changed test players
        Dim temp As String = myPlayer.Replace("(u", "  ")

        Return temp
    End Function


    Public Function GetPlayerKills(ByVal myKills As String) As String
        Dim reg As Regex = New Regex("]+\sKills:+\s+\w+")
        Dim m As Match = reg.Match(myKills)
        myKills = m.Value.TrimStart("]")
        myKills = myKills.Replace("Kills: ", "")

        Return myKills
    End Function
    Public Function GetPlayerTeamKills(ByVal myTk As String) As String
        Dim reg As Regex = New Regex("m\s*\sKills:\s*\w*")
        Dim m As Match = reg.Match(myTk)
        Dim temp As String = m.Value.Replace("m ", "")
        temp = temp.Replace("Kills:", "")
        Return temp
    End Function
    Public Shared Function fGETDATE(ByVal myString As String) As String
        Dim reg As Regex = New Regex("((?<=\[)[^\[\]]*)")
        Dim m As Match = reg.Match(myString)
        myString = m.Value.ToString
        reg = Nothing

        Return myString
    End Function
    Public Function Players() As Collection
        Return m_Players
    End Function
    Public Sub RemovePlayer(ByVal index As Integer)
        m_Players.Remove(index)
    End Sub
    Public Function GetPLAYERCount() As Integer
        Return m_Players.Count
    End Function
    Public Function GetPLAYERObject(ByVal index As Integer) As CPlayer
        Return m_Players.Item(index)
    End Function

End Class
 
Last edited:
Stumped? I waS thinking I want to turn this into a WEBSERVICE.So the class takes care of all the internals.The webpage just calls in the stats.Any thought and or ideas?
 
AFterlife, I don't want to dump too hard on you or anything, but you might find more success by starting a new thread rather than hijacking this one. This thread is covering techniques for searching through a list of objects not using predicates to help refactor code. Generally speaking, it's easier to navigate the forums if one thread == one subject.

I'm sure if you were to start a new thread with a more appropriate subject header you'll grab more people's attention and probably get more responses. :)
 
Back
Top