jpavlov
jpavlov

Reputation: 2261

Check if value exist in a List C#

I have a DataTable that is filled by a Stored Procedure, and from that datatable which contains a collection of Requests(RequestNumber and Tasks(TaskId). When I have reach the first Request number with a Task, I add it to my list, then with additional datarows, I check the list to see if they exist(if(dr["RequestNumber"].ToString() != acList[i].RequestNumber)) if they do, I delete the dataRow, if not I add them to the list.

This works good in sequential order, but if the datarow and list are off by one it allows the row to be added. Is there any other way to accomplish finding if the value exists in the list.

Thanks in advance.

foreach (DataRow dRow in dt.Rows)
{
    DataRow dr = dt.NewRow();
    dr["Project"] = dRow["Project"];
    dr["RequestNumber"] = dRow["RequestNumber"];
    dr["RequestId"] = dRow["RequestId"];
    dr["TaskType"] = dRow["TaskType"];
    dr["TaskId"] = dRow["TaskId"];
    dr["TaskStatus"] = dRow["TaskStatus"];
    dr["AssignedTo"] = dRow["AssignedTo"];
    dr["DateDue"] = dRow["DateDue"];


    if (acList.Count == 0)
    {
        acList.Add(new AssignedClass
        {
            Project = dr["Project"].ToString(),
            RequestNumber = dr["RequestNumber"].ToString(),
            RequestId = dr["RequestId"].ToString(),
            TaskType = dr["TaskType"].ToString(),
            TaskId = dr["TaskId"].ToString(),
            TaskStatus = dr["TaskStatus"].ToString(),
            AssignedTo = dr["AssignedTo"].ToString(),
            DateDue = dr["DateDue"].ToString()
        });
    }

    else
    {
        for (int i = 0; i < acList.Count; i++)
        {

        if(dr["RequestNumber"].ToString() != acList[i].RequestNumber)
        {
            acList.Add(new AssignedClass
            {
                Project = dr["Project"].ToString(),
                RequestNumber = dr["RequestNumber"].ToString(),
                RequestId = dr["RequestId"].ToString(),
                TaskType = dr["TaskType"].ToString(),
                TaskId = dr["TaskId"].ToString(),
                TaskStatus = dr["TaskStatus"].ToString(),
                AssignedTo = dr["AssignedTo"].ToString(),
                DateDue = dr["DateDue"].ToString()
            });
        }
        else
        {
            dr.Delete();
        }
      }
    }

Upvotes: 4

Views: 14900

Answers (4)

Ash Burlaczenko
Ash Burlaczenko

Reputation: 25475

With the option of linq and taking into account that the beginning code block and the check for 0 entries seem a bit redundant. I think the process could boil down to

var distinctRows = dt.Rows.GroupBy(x => x["RequestNumber"]).Select(x => x.First());
acList.AddRange(distinctRows.Select(x => x.MapToAssignedClass());


// Added Mapping method for readability
public static AssignedClass MapToAssignedClass(this DataRow dr)
{
    return new AssignedClass
    {
        Project = dr["Project"].ToString(),
        RequestNumber = dr["RequestNumber"].ToString(),
        RequestId = dr["RequestId"].ToString(),
        TaskType = dr["TaskType"].ToString(),
        TaskId = dr["TaskId"].ToString(),
        TaskStatus = dr["TaskStatus"].ToString(),
        AssignedTo = dr["AssignedTo"].ToString(),
        DateDue = dr["DateDue"].ToString()
    });
}

Upvotes: 0

Honza Brestan
Honza Brestan

Reputation: 10957

This would be a great job for LINQ's Union method, but it requires an IEqualityComparer<AssignedClass> implementation. Unless you do this often, it's probably not worth coding (even though it's 10-ish lines if done properly). This would help, however:

acList = acList
    .Concat(from row in dt.Rows
            from ac in acList
            where ac.RequestNumber != row["RequestNumber"].ToString()
            select AssignedClassFromDataRow(row))
    .ToList();

where

private static AssignedClass AssignedClassFromDataRow(DataRow row)
{
    // maybe some checks...
    return new AssignedClass
    {
        Project = dRow["Project"].ToString(),
        RequestNumber = dRow["RequestNumber"].ToString(),
        RequestId = dRow["RequestId"].ToString(),
        TaskType = dRow["TaskType"].ToString(),
        TaskId = dRow["TaskId"].ToString(),
        TaskStatus = dRow["TaskStatus"].ToString(),
        AssignedTo = dRow["AssignedTo"].ToString(),
        DateDue = dRow["DateDue"].ToString()
    }
}

Slightly more time complex than a hash-based solution, but simple enough to implement.

EDIT:

If you actually need the extra performance provided by hashing, you can write the EqualityComparer (but keep in mind these guidelines). Such solution would look like this in the end:

acList = acList
    .Union(
        dt.Rows.Select(AssignedClassFromDataRow),
        new MyAssignedClassRequestNumberComparer())
    .ToList();

Upvotes: 2

Woodman
Woodman

Reputation: 1137

You can use HashSet<AssignedClass>, all you need is to create custom IEqualityComarer<AssignedClass> in which you check RequestNumber property of passed objects, and pass instance of this comparer in constructor of HashSet

Edit

Here is possible implementation of IEqualityComarer<AssignedClass> :

public class AssignedClassComparer : IEqualityComparer<AssignedClass>
{
    public bool Equals(AssignedClass x, AssignedClass y)
    {
        return x.RequestNumber == y.RequestNumber;
    }

    public int GetHashCode(AssignedClass obj)
    {
        return obj.RequestNumber.GetHashCode();
    }
}

EDIT2: Or you can simply use HashSet to store only keys, while enumerating through rows:

var keys = new HashSet<string>();

foreach (DataRow dRow in dt.Rows)
{
    if (keys.Add(dRow["RequestNumber"].ToString()))
    {
        acList.Add(new AssignedClass
        {
            Project = dRow["Project"].ToString(),
            RequestNumber = dRow["RequestNumber"].ToString(),
            RequestId = dRow["RequestId"].ToString(),
            TaskType = dRow["TaskType"].ToString(),
            TaskId = dRow["TaskId"].ToString(),
            TaskStatus = dRow["TaskStatus"].ToString(),
            AssignedTo = dRow["AssignedTo"].ToString(),
            DateDue = dRow["DateDue"].ToString()
        });
    }
}

Upvotes: 0

mellamokb
mellamokb

Reputation: 56779

Using LINQ, it's as simple as checking if there are any matches:

if ( !acList.Any(a => a.RequestNumber == dr["RequestNumber"].ToString() )
    acList.Add( ... );

Also, it seems that the code at the beginning assigning dRow to dr has no purpose. Just use dRow directly throughout the rest of your code. And I don't think you want to treat (acList.Count == 0) as a special case, because that just causes you to have to duplicate your logic and thus maintain two separate copies of the same code. So if I understood everything correctly, this simplified code should accomplish the same thing:

foreach (DataRow dRow in dt.Rows)
{
    if ( !acList.Any(a => a.RequestNumber == dRow["RequestNumber"].ToString() )
    {
        acList.Add(new AssignedClass
        {
            Project = dRow["Project"].ToString(),
            RequestNumber = dRow["RequestNumber"].ToString(),
            RequestId = dRow["RequestId"].ToString(),
            TaskType = dRow["TaskType"].ToString(),
            TaskId = dRow["TaskId"].ToString(),
            TaskStatus = dRow["TaskStatus"].ToString(),
            AssignedTo = dRow["AssignedTo"].ToString(),
            DateDue = dRow["DateDue"].ToString()
        });
    }
}

Upvotes: 5

Related Questions