3

您好,我正在尝试在从 XML 文件填充的两个 dataGridView 表中查找唯一字符串。我制作的代码运行没有问题,但是当我在其中一个表中更改字符串(使其唯一)时,它无法检测到。我的逻辑有什么问题吗?

    private void button5_Click(object sender, EventArgs e)
    {
        string[] column1 = new string[dataGridView1.Rows.Count];
        string[] column2 = new string[dataGridView2.Rows.Count];
        int unique = 0;
        bool found = false;
        for (int i = 0; i < dataGridView1.Rows.Count; i++)
        {
            column1[i] = Convert.ToString(dataGridView1.Rows[i].Cells[2].Value);
        }
        for (int i = 0; i < dataGridView2.Rows.Count; i++)
        {
            column2[i] = Convert.ToString(dataGridView2.Rows[i].Cells[2].Value);
        }
        for (int i = 0; i < column1.Length; i++)
        {
            for (int j = 0; j < column2.Length; j++)
            {
                if (column1[i] == column2[j])
                {
                    found = true;
                }
            }
            if (found == false)
            {
                unique++;
                found = false;
            }
        }
        MessageBox.Show(unique + " unique strings found!"); 
    }

最终解决方案需要能够返回包含唯一字符串的单元格,以便我可以向用户突出显示它们。非常感谢你的帮助!

4

5 回答 5

3

使用 linq 很容易:

array1.Except(array2).Concat(array2.Except(array1))

作为对您的评论的回应,您可以模拟具有两个左连接的完整外连接,并在输出中查找空值。连接的另一侧不匹配的任何一侧都可以被认为是唯一的。使用以下扩展进行连接:

public static class LinqEx
{
    public static IEnumerable<TResult> 
        LeftOuterJoin<TOuter, TInner, TKey, TResult>(
            this IEnumerable<TOuter> outer, 
            IEnumerable<TInner> inner, 
            Func<TOuter, TKey> outerKeySelector, 
            Func<TInner, TKey> innerKeySelector, 
            Func<TOuter, TInner, TResult> resultSelector)
    {
        return outer
            .GroupJoin(
                inner, 
                outerKeySelector, 
                innerKeySelector, 
                (a, b) => new
                {
                    a,
                    b
                })
            .SelectMany(
                x => x.b.DefaultIfEmpty(), 
                (x, b) => resultSelector(x.a, b));
    }

    public static IEnumerable<TResult> 
        FullOuterJoin<TSet1, TSet2, TKey, TResult>(
            this IEnumerable<TSet1> set1, 
            IEnumerable<TSet2> set2, 
            Func<TSet1, TKey> set1Selector, 
            Func<TSet2, TKey> set2Selector, 
            Func<TSet1, TSet2, TResult> resultSelector)
    {
        var leftJoin = set1.
            LeftOuterJoin(
                set2, 
                set1Selector, 
                set2Selector, 
                (s1, s2) => new {s1, s2});
        var rightJoin = set2
            .LeftOuterJoin(
                set1, 
                set2Selector, 
                set1Selector, 
                (s2, s1) => new {s1, s2});
        return leftJoin.Union(rightJoin)
            .Select(x => resultSelector(x.s1, x.s2));

    }
}

然后,您可以创建匿名对象来捕获附加数据,例如项目的索引和它的来源,并外连接它们。连接两边都有项目的结果将被过滤掉(因为它们同时存在于两个集合中),因此结果集现在只包含其中一个集合唯一的项目。

void Main()
{
    var set1 = new[] {"a", "b", "c"};
    var set2 = new[] {"b", "c", "d", "d"};
    var annotatedSet1 = set1
        .Select((item,index) => new {src = "set1", index, item});
    var annotatedSet2 = set2
        .Select((item,index) => new {src = "set2", index, item});

    var uniques = annotatedSet1
        .FullOuterJoin(
            annotatedSet2, 
            x => x.item, 
            x => x.item,
            (s1, s2) => new {s1, s2})
        .Where(x => x.s1 == null || x.s2 == null)
        .Select(x => x.s1 ?? x.s2);
}

这将产生结果:

{src="set1", index=0, item="a"}
{src="set2", index=2, item="d"}
{src="set2", index=3, item="d"}
于 2012-08-22T13:18:33.423 回答
0

您可以使用链接运算符、联合运算符和不同运算符

var temp =  column1.Union(column2);
var result   = temp.Distinct();
于 2012-08-22T13:18:15.433 回答
0

这是 Linq 的方式Enumerable.Except

var dg1Cell2 = dataGridView1
    .Rows.Cast<DataGridViewRow>()
    .Select(r => r.Cells[2].Value.ToString());
var dg2Cell2 = dataGridView1
    .Rows.Cast<DataGridViewRow>()
    .Select(r => r.Cells[2].Value.ToString());
var uniqueInDG1 = dg1Cell2.Except(dg2Cell2);
var result = from r in dataGridView1.Rows.Cast<DataGridViewRow>()
             join u in uniqueInDG1 on r.Cells[2].Value.ToString() equals u
             select r;

Except产生一组差异,因此只保留第一个序列中的唯一字符串。然后我将结果加入到DataGridViewRows.

它比看起来更有效,因为它都是惰性执行的,除了在set内部使用 a 并且join在 linq-to-objects 中也非常有效

于 2012-08-22T13:20:08.850 回答
0

感谢您的所有回复,但简单的答案是我没有在正确的点将 found 的值重置为 false。我很欣赏您的解决方案所付出的努力,但对于我的知识水平来说,它们都太复杂了。

这是调整后的代码:

    private void button5_Click(object sender, EventArgs e)
    {
        string[] column1 = new string[dataGridView1.Rows.Count];
        string[] column2 = new string[dataGridView2.Rows.Count];
        int unique = 0;
        bool found = false;
        for (int i = 0; i < dataGridView1.Rows.Count; i++)
        {
            column1[i] = Convert.ToString(dataGridView1.Rows[i].Cells[2].Value);
        }
        for (int i = 0; i < dataGridView2.Rows.Count; i++)
        {
            column2[i] = Convert.ToString(dataGridView2.Rows[i].Cells[2].Value);
        }
        for (int i = 0; i < column1.Length; i++)
        {
            for (int j = 0; j < column2.Length; j++)
            {
                if (column1[i] == column2[j])
                {
                    found = true;
                }
            }
            if (found == false)
            {
                unique++;
            }
            found = false;
        }
        MessageBox.Show(unique + " unique strings found!");         
    }
于 2012-08-22T13:54:37.097 回答
0

以下代码将从 2 个字符串数组中返回不同的字符串数组

public string[] UniqueNames(string[] names1, string[] names2) {
    newName = names1.ToList();
    foreach (var dr in names2) {
        if (!newName.Contains(dr)) {
            newName.Add(dr);
        }
    }
    string[] newNameArray = newName.ToArray();
    return newNameArray;
}

或者你也可以试试

public string[] UniqueNames(string[] names1, string[] names2) {            
    return names1.Union(names2).ToArray(); ;            
}
于 2020-12-19T13:04:51.517 回答