在列表中查找重复但有条件

想要从列表中删除重复项,以便我的列表包含:

www.test.com test.com mytest.com 

我希望最终列表如下所示(仅从前面的副本中选择带有www的域):

 www.test.com mytest.com 

我有这个linq,但似乎忽略了所有没有www前面的域名,因为它只选择了www:

 var result=inputList.Where(x=>x.DomainName.StartsWith("www.")).Distinct(); 

编辑:

@DanielHilgarth:我只是运行你的代码并没有产生正确的结果。 我有 :

 test.com www.test.com test2.com www.test2.com test3.com www.test3.com test4.com 

在我的清单中。 它返回:

 test.com www.test.com www.test2.com www.test3.com 

以下是我如何使用您的代码:

 var result = lstServerBindings.GroupBy(x => x.DomainName.StartsWith("www.") ? x.DomainName : "www." + x) .Select(x => { var domain = x.FirstOrDefault(y => y.DomainName.StartsWith("www.")); if (domain == null) return x.First(); return domain; }); 

然后我做一个foreach循环分配到新列表:

 foreach (var item in result) { lstUniqueServerBindings.Add(new ServerBindings { IPAddress = item.IPAddress, PortNumber = item.PortNumber, DomainName = item.DomainName }); } 

我想你想要这样的东西:

 var result = domains.GroupBy(x => x.StartsWith("www.") ? x : "www." + x) .Select(x => { var domain = x.FirstOrDefault(y => y.StartsWith("www.")); if(domain == null) return x.First(); return domain; }); 

我用这个输入测试了它:

 var domains = new List { "www.test.com", "test.com", "mytest.com", "abc.com", "www.abc.com" }; 

结果是:

 www.test.com mytest.com www.abc.com 

您的代码应如下所示(请注意第二行末尾的其他.DomainName ):

 var result = lstServerBindings.GroupBy(x => x.DomainName.StartsWith("www.") ? x.DomainName : "www." + x.DomainName) .Select(x => { var domain = x.FirstOrDefault(y => y.DomainName.StartsWith("www.")); if (domain == null) return x.First(); return domain; }); 

顺便说一句:您可以通过将代码更改为以下内容来保存自己的foreach循环:

 var result = lstServerBindings.GroupBy(x => x.DomainName.StartsWith("www.") ? x.DomainName : "www." + x.DomainName) .Select(x => { var item = x.FirstOrDefault(y => y.DomainName.StartsWith("www.")); if (item == null) item = x.First(); return new ServerBindings { IPAddress = item.IPAddress, PortNumber = item.PortNumber, DomainName = item.DomainName }; }); 

这是一个棘手的问题,但有一个相当直接的解决方案:

  public class wwwOrderComparison : IComparer { public int Compare(string x, string y) { if(x == null && y == null) return 0; if(x == null ^ y == null) return 0; var xWww = x.StartsWith("www"); var yWww = y.StartsWith("www"); return (xWww && x == "www." + y) ? -1 : ((yWww && "www." + x == y) ? 1 : 0); } } public class wwwEqualityComparison : IEqualityComparer { public bool Equals(string x, string y) { if (x == null && y == null) return true; if (x == null ^ y == null) return false; var xWww = x.StartsWith("www"); var yWww = y.StartsWith("www"); if (xWww ^ yWww) return xWww ? (x == "www." + y) : ("www." + x == y); return xWww == yWww; } public int GetHashCode(string obj) { return (obj.StartsWith("www.") ? obj : ("www." + obj)).GetHashCode(); } } 

这是测试:

  var list = new List { "www.test.com", "test.com", "mytest.com", "abc.com", "www.abc.com", "zzz.com", "www.zzz.com" }; var s = list.OrderBy(t => t, new wwwOrderComparison()).Distinct(new wwwEqualityComparison()).ToList(); 

这已通过我的所有测试。 干杯第二次:)

编辑:请参阅下面的Daniel的回复。 我对这个有点太匆忙。

使用“选择”可以对元素进行投影,选择/修改某些属性。 这可能听起来很复杂,但您需要做的就是:

 inputList.Select(x => x.Replace("www.", "")).Distinct() 

应该管用!

编辑:一点解释。 使用select,您基本上可以将旧对象映射为新对象,然后为查询选择这些对象。 在上面的情况下,您只选择一个简单的字符串对象,您可以使用以下内容创建一个全新的对象类型:

 Select(x => new { Content = x, ContentLength = x.Length, ContentType = x.GetType() }) 

在这里,您可以根据输入对象的不同属性和方法动态构建一个新对象。 选择是非常有用和强大的!

重新定义相等意义的正常.NET方法(实质上就是你在这里做的)是实现IEqualityComparer

 private class IgnoreWWWEqComparer : IEqualityComparer { public bool Equals(string x, string y) { if(ReferenceEquals(x, y)) return true; if(x == null || y == null) return false; if(x.StartsWith("www.")) { if(y.StartsWith("www.")) return x.Equals(y); return x.Substring(4).Equals(y); //the above line can be made faster, but this is a reasonable //approach if performance isn't critical } if(y.StartsWith("www.")) return x.Equals(y.Substring(4)); return x.Equals(y); } public int GetHashCode(string obj) { if(obj == null) return 0; if(obj.StartsWith("www.")) return obj.Substring(4).GetHashCode(); return obj.GetHashCode(); } } 

现在Distinct()做你想要的:

 var result=inputList.OrderBy(s => !s.StartsWith("www.")).Distinct(new IgnoreWWWEqComparer()); 

对于一次性,您可能会发现只需将字符串与任何起始www. group by就更方便了www. 删除并选择每个分组的第一个,但上面应该更快地丢弃找到的重复项,当然IgnoreWWWEqComparer可以重复使用。

编辑:

考虑到“www。”的要求。 表格优先,然后上面的情况很好,但是如果我们有一个非常大的列表可以处理的话,我会认为它会很糟糕。 如果我们对性能非常欺负,我们想要使我们的EqualsGetHashCode变得更好,但是可能对大量列表进行排序对于几十个很好,但是在一段时间后开始受到伤害。 因此,如果只有一小部分(只是为了更简单),以下不是我采取的方法,但如果它可能非常大:

 public static IEnumerable FavourWWWDistinct(IEnumerable src) { Dictionary dict = new Dictionary(new IgnoreWWWEqComparer()); foreach(string str in src) { bool withWWW; if(dict.TryGetValue(str, out withWWW)) { if(withWWW) continue; if(str.StartsWith("www.")) { dict[str] = true; yield return str; } } else { if(dict[str] = str.StartsWith("www.")) yield return str; } } foreach(var kvp in dict) if(!kvp.Value) yield return kvp.Key; } 

这样我们就会以"www."开头传递那些表格"www." 只要我们看到它们,只有那些不以它开头的那些必须等待整个列表被处理。