在列表中查找重复项,但有条件
本文关键字:有条件 列表 查找 | 更新日期: 2023-09-27 18:20:09
想要从列表中删除重复项,以便如果我的列表包含:
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<string>
{
"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<String>
{
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<String>
{
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<String> {
"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();
这已经通过了我所有的测试。 第二次欢呼:)
编辑:请参阅下面的丹尼尔的回复。我在这个问题上有点太匆忙了。
使用 Select 对元素进行投影,选择/修改某些属性。这听起来可能很复杂,但您需要做的就是:
inputList.Select(x => x.Replace("www.", "")).Distinct()
应该工作!
编辑:一点解释。使用 select,您基本上可以将旧对象映射到新对象,然后选择这些对象进行查询。在上面的情况下,您只选择一个简单的字符串对象,您可以使用以下内容创建一个全新的对象类型:
Select(x => new { Content = x, ContentLength = x.Length, ContentType = x.GetType() })
在这里,您可以根据输入对象的不同属性和方法动态构造一个新对象。选择非常有用且功能强大!
重新定义相等含义的正常 .NET 方法(本质上是您在此处所做的(是实现 IEqualityComparer<T>
.
private class IgnoreWWWEqComparer : IEqualityComparer<string>
{
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());
对于一次性的,您可能会发现只group by
删除任何起始www.
的字符串并选择每个分组中的第一个更方便,但上面的内容应该更快地丢弃找到的重复项,当然IgnoreWWWEqComparer
可以重用。
编辑:
考虑到"www."表单优先的要求,那么以上很好,但是考虑到如果我们有一个非常大的列表来处理它会很糟糕,这确实有点困扰我。如果我们真的欺负性能,我们希望让我们的Equals
和GetHashCode
变得更好,但可能对几十个人来说排序是可以的,但一段时间后就会开始受到伤害。因此,如果只有一小部分(只是更简单(,我会采取以下方法,但如果它可以非常大:
public static IEnumerable<string> FavourWWWDistinct(IEnumerable<string> src)
{
Dictionary<string, bool> dict = new Dictionary<string, bool>(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."
开头的表单,只有那些不以它开头的表单才需要等待整个列表被处理。