在C#中检查值是否存在于列表中

4
我有一个DataTable,其中填充了一个存储过程的集合,包括Requests(RequestNumber和Tasks(TaskId))。当我到达第一个具有任务的请求编号时,我将其添加到我的列表中,然后通过附加数据行,检查列表以查看它们是否存在(如果(dr["RequestNumber"].ToString() != acList[i].RequestNumber)),如果存在,则删除数据行,否则将它们添加到列表中。
这在顺序执行时有效,但如果数据行和列表相差一个,则允许添加该行。是否有其他方法来找到列表中是否存在该值?
提前致谢。
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();
        }
      }
    }

1
你使用的是哪个版本的.NET?你能使用LINQ吗? - mellamokb
4
从头开始说起。你有一个DataTable,你想对它做什么?你的代码看起来比实际需要复杂一些。 - Ash Burlaczenko
4.0和我有使用Linq的能力,但我对该语言不是太熟悉。 - jpavlov
就像@AshBurlaczenko所说的那样,您需要退后一步并重新实现您的逻辑。从编码角度来看,这似乎有些过度了。拿起笔写下逻辑应该如何,并重新实现它。 - codingbiz
我没有看到“List”,那么使用“List”的代码在哪里?如果你想得到帮助,你必须告诉我们DataTable中期望的类型。 - Security Hound
4个回答

5
使用LINQ,只需检查是否有任何匹配项即可:
if ( !acList.Any(a => a.RequestNumber == dr["RequestNumber"].ToString() )
    acList.Add( ... );

此外,似乎一开始将dRow分配给dr的代码没有意义。在接下来的代码中直接使用dRow即可。我认为您不希望将(acList.Count == 0)视为特殊情况,因为这会导致您必须复制逻辑并因此维护两个相同代码的副本。因此,如果我正确理解了一切,这个简化后的代码应该可以实现相同的功能:
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()
        });
    }
}

这种方法并不太有效。因为对于每一行,您需要再次扫描acList中的所有项目。因此,如果有很多行,这可能会降低性能。 - Woodman
@Woodman:除非有成千上万行代码,否则你才会开始注意到性能问题。我更喜欢先编写简单易读的代码,然后在分析(而不是猜测)真正的性能问题所在之后再进行优化。 - mellamokb
完全同意你的观点,只是认为值得一提。 - Woodman

2
这将是使用 LINQ 的 Union 方法的一个很好的工作,但它需要一个 IEqualityComparer<AssignedClass> 实现。除非你经常这样做,否则编码可能不值得(即使如果正确地完成,也只有大约 10 行代码)。然而,这会有所帮助:
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()
    }
}

比基于哈希的解决方案稍微复杂一些,但实现起来足够简单。
编辑:
如果您确实需要哈希提供的额外性能,则可以编写EqualityComparer(但请记住这些指南)。最终,这种解决方案将如下所示:
acList = acList
    .Union(
        dt.Rows.Select(AssignedClassFromDataRow),
        new MyAssignedClassRequestNumberComparer())
    .ToList();

0

你可以使用 HashSet<AssignedClass>,你只需要创建一个自定义的 IEqualityComarer<AssignedClass>,在其中检查传递对象的 RequestNumber 属性,并将此比较器的实例传递给 HashSet 的构造函数。

编辑

这里是 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();
    }
}

编辑2: 或者你可以简单地使用 HashSet 仅存储键,同时枚举行:

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()
        });
    }
}

0

考虑到linq选项以及起始代码块和检查0条目似乎有点冗余,我认为这个过程可以简化为

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()
    });
}

1
你从 dt 中得到了不同的行,但仍需检查在将 dtacList 合并后是否有重复项。使用 AddRange 可能会创建这样的重复项。 - Honza Brestan
@HonzaBrestan,您是否在说acList可能已经包含项目?如果是这种情况,那么是的,您是正确的,但我没有从问题中得到这种情况。 - Ash Burlaczenko
请检查提供的代码,acList.Count 上有一个条件,并且只有在它不为空时才检查重复项。 - Honza Brestan
@HonzaBrestan,我看到提供的代码。从中没有任何东西表明在输入代码之前acList包含项。按照我的理解,acList.Count是用于循环的第一次迭代。 - Ash Burlaczenko
可能是,也可能不是 - 这只是一种假设。 - Honza Brestan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接