C# 如何通过自定义子字符串等式仅迭代不同的字符串值

C# 如何通过自定义子字符串等式仅迭代不同的字符串值,c#,linq,iequalitycomparer,C#,Linq,Iequalitycomparer,和问题类似,我尝试只迭代给定字符串的子字符串的不同值,例如: List<string> keys = new List<string>() { "foo_boo_1", "foo_boo_2, "foo_boo_3, "boo_boo_1" } public int GetHashCode(string obj) { var index = obj.LastIndexOf("_"); return obj.Substrin

和问题类似,我尝试只迭代给定字符串的子字符串的不同值,例如:

List<string> keys = new List<string>()
{
    "foo_boo_1",
    "foo_boo_2,
    "foo_boo_3,
    "boo_boo_1"
}
public int GetHashCode(string obj)
{
    var index = obj.LastIndexOf("_");
    return obj.Substring(0, index).GetHashCode();
}
var keys = new List<string>()
{
    "foo_boo_1",
    "foo_boo_2",
    "foo_boo_3",
    "boo_boo_1"
};

var distinct = keys
    .Select(k => new
    {
        original = k,
        truncated = k.Contains("_") ? k.Substring(0, k.LastIndexOf("_")) : k
    })
    .GroupBy(k => k.truncated)
    .Select(g => g.First().original);
但结果是:

福布斯1
福布斯2
福布斯3
boo_boo_1

使用
IEqualityComparer
如何删除子字符串的不同值(
foo\u boo\u 2
foo\u boo\u 3
)?


*请注意,“真实”键要长得多,类似于“1_0_8-B153_GF_6_2”,因此我必须使用LastIndexOf

等式比较器中的
GetHashCode
方法返回整个字符串的哈希代码,只需对子字符串进行哈希,例如:

List<string> keys = new List<string>()
{
    "foo_boo_1",
    "foo_boo_2,
    "foo_boo_3,
    "boo_boo_1"
}
public int GetHashCode(string obj)
{
    var index = obj.LastIndexOf("_");
    return obj.Substring(0, index).GetHashCode();
}
var keys = new List<string>()
{
    "foo_boo_1",
    "foo_boo_2",
    "foo_boo_3",
    "boo_boo_1"
};

var distinct = keys
    .Select(k => new
    {
        original = k,
        truncated = k.Contains("_") ? k.Substring(0, k.LastIndexOf("_")) : k
    })
    .GroupBy(k => k.truncated)
    .Select(g => g.First().original);

您当前的实现存在一些缺陷:

  • Equals
    GetHashCode
    都不能抛出异常(必须检查
    null
  • 如果
    Equals
    x
    y
    返回
    true
    ,则
    GetHashCode(x)==GetHashCode(y)
    。反例是
    “abc_1”
    “abc_2”
  • 第二个错误很可能导致
    Distinct
    返回不正确的结果(
    Distinct
    第一个计算哈希)

    正确的代码可以是这样的

    public class MyEqualityComparer : IEqualityComparer<string> {
      public bool Equals(string x, string y) {            
        if (ReferenceEquals(x, y))
          return true;
        else if ((null == x) || (null == y))
          return false;
    
        int xIndex = x.LastIndexOf('_'); 
        int yIndex = y.LastIndexOf('_');
    
        if (xIndex >= 0)         
          return (yIndex >= 0) 
            ? x.Substring(0, xIndex) == y.Substring(0, yIndex)
            : false;
        else if (yIndex >= 0)         
          return false;
        else
          return x == y; 
      }
    
      public int GetHashCode(string obj) {
        if (null == obj)  
          return 0;
    
        int index = obj.LastIndexOf('_');
    
        return index < 0 
          ? obj.GetHashCode() 
          : obj.Substring(0, index).GetHashCode();
      }
    }
    

    要获得更简洁的解决方案,避免使用自定义的
    IEqualityComparer
    ,您可以使用
    GroupBy
    。例如:

    List<string> keys = new List<string>()
    {
        "foo_boo_1",
        "foo_boo_2,
        "foo_boo_3,
        "boo_boo_1"
    }
    
    public int GetHashCode(string obj)
    {
        var index = obj.LastIndexOf("_");
        return obj.Substring(0, index).GetHashCode();
    }
    
    var keys = new List<string>()
    {
        "foo_boo_1",
        "foo_boo_2",
        "foo_boo_3",
        "boo_boo_1"
    };
    
    var distinct = keys
        .Select(k => new
        {
            original = k,
            truncated = k.Contains("_") ? k.Substring(0, k.LastIndexOf("_")) : k
        })
        .GroupBy(k => k.truncated)
        .Select(g => g.First().original);
    
    var keys=new List()
    {
    “foo_boo_1”,
    “foo_boo_2”,
    “foo_boo_3”,
    “boo_boo_1”
    };
    var distinct=键
    .选择(k=>new
    {
    原始=k,
    截断=k.Contains(“\u”)?k.Substring(0,k.LastIndexOf(“\u”):k
    })
    .GroupBy(k=>k.截断)
    .选择(g=>g.First().original);
    
    这将产生:

    福布斯1

    boo_boo_1


    嘿@DavidG,谢谢,
    等于(…)
    ?我可以将其保留为NotImplementedException吗?
    Equals
    方法甚至不会被调用,因为您的所有字符串都不同。进行此更改,它将是。当调用Equals时?你知道,你可以调试此代码并自己解决它,对吗?我已经尝试在
    Equals(…)
    中设置断点,但它不会中断。当我将其设置为
    NotImplementedException
    时,程序崩溃。只有当
    等于(…)
    引用
    GetHashCode(…)
    时,它才有效,谢谢。嘿@dmitry,回答得很好。你能解释一下为什么断点在
    Equals(…)
    中没有中断吗?@Shahar Shokrani:
    Distinct
    分两个阶段比较
    x
    y
    :第一阶段比较散列;如果散列码不同,则不需要调用
    Equals
    ,只有当散列码相等时,它才会运行
    Equals
    。我们的散列很好,看起来我们根本不需要
    等于
    ,但是索引是-1呢