Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 根据较小比较字符串中的最小N个字符确定重复项_C#_String_Linq_Duplicates - Fatal编程技术网

C# 根据较小比较字符串中的最小N个字符确定重复项

C# 根据较小比较字符串中的最小N个字符确定重复项,c#,string,linq,duplicates,C#,String,Linq,Duplicates,我有两个列表,都包含共享公共字段ID(字符串值)的模型。我正在比较ID的重复 我目前有一个LINQ语句来确定重复的ID值,它将它们存储到字符串列表中: List<string> duplicateRecords = testData.TestRecords.GroupBy(aa => aa.ID).Where(x => x.Count() > 1).Select(y => y.Key).ToList(); List duplicateRecords=test

我有两个列表,都包含共享公共字段ID(字符串值)的模型。我正在比较ID的重复

我目前有一个LINQ语句来确定重复的ID值,它将它们存储到字符串列表中:

List<string> duplicateRecords = testData.TestRecords.GroupBy(aa => aa.ID).Where(x => x.Count() > 1).Select(y => y.Key).ToList();
List duplicateRecords=testData.TestRecords.GroupBy(aa=>aa.ID).Where(x=>x.Count()>1).选择(y=>y.Key).ToList();
以及第二个LINQ语句,该语句基于重复的ID LINQ结果映射受尊重模型的列表:

List<Model> modelRecords = testData.Models.Where(x => duplicateRecords.Any(y => x.ID == y)).ToList();
List modelRecords=testData.Models.Where(x=>duplicateRecords.Any(y=>x.ID==y)).ToList();
这两个LINQ语句完全符合我的预期,非常好。但现在有一个最近的请求,在比较过程中根据重复ID的最小N个字符来确定重复ID。必须对字符串中的最后N个字符进行最小N比较

EX)

  • ID1:123=ID2:123
  • ID1:0123==ID2:123
  • ID1:123=ID2:0123
  • ID1:1230!=ID2:123
  • ID1:123!=ID2:1230
  • ID1:122110123==ID2:123
希望这些例子能让我对我试图解决的问题有所了解。这可以通过使用
foreach
循环来实现,但我已经体验到,在复杂的列表查询中,代码变得非常混乱和难以管理

所以我的问题是:如何使用两个比较字符串中较小字符串的最后N个字符来确定使用LINQ的重复项


注意:我也非常愿意采用更优雅的方法来解决这个问题,非常希望排除任何
for
foreach
解决方案。

我假设ID是字符串。如果是,您可以这样做:

string match = "123";

var duplicate = list.Where(x=> x.Substring(x.Length - match.Length) == match).ToList();

如果我正确理解了您的问题,那么看起来只需要在分组时切掉每个
ID
属性中的最后N个字符

大概是这样的:

使用系统;
使用System.Linq;
公共类测试记录
{
公共字符串ID{get;set;}
}
公共类测试模型
{
公共字符串ID{get;set;}
}
公共课程
{
公共静态void Main()
{
var N=3;//这是定义所需N长度的地方
var rand=new Random();
var testRecords=新的TestRecord[]
{
新的TestRecord{ID=“123”},
新的TestRecord{ID=“0123”},
新的TestRecord{ID=“1230”},
新的TestRecord{ID=“122110123”},
};
var testModels=新的TestModel[]
{
新的测试模型{ID=“123”},
新的测试模型{ID=“0123”},
新的测试模型{ID=“1230”},
新的测试模型{ID=“122110123”},
};
bool SortEm(字符串a,字符串b)=>a.长度testRecords.Any(target=>record.ID!=target.ID&&SortEm(target.ID,record.ID)))
.ToDictionary(
key=>key,
key=>testModels.Where(testModel=>SortEm(key.ID,testModel.ID)).ToArray();
foreach(模型中的var kvp)
{
System.Console.WriteLine($”用于重复键({kvp.key.ID})找到的模型:\r\n\t{string.Join(“\r\n\t”,kvp.Value.Select(x=>x.ID)));
}
}
}

我假设当输入包含
123
0123
时,您希望结果同时包含这两个属性

var input = new List<Model>()
{
    new Model {ID = "123"},
    new Model {ID = "0123"},
    new Model {ID = "1230"},
    new Model {ID = "12"},
    new Model {ID = "122110123"}
};

var result = input.Where(x => input.Any(y => y != x && (y.ID.EndsWith(x.ID) || x.ID.EndsWith(y.ID)))).ToList();
\\this will return 123, 0123 and 122110123

为了有效地找到重复项,您需要按长度对
ID
s进行排序,以便尽可能减少必要的比较。(排序增加了一些开销,但大大减少了必须进行的比较——在我的测试中,9个ID有8个值,3个是8个值的重复项,排序的是15个比较项,而未排序的是42个比较项。)一旦按长度排序,只需将每个比较项与等于或更长的比较(如果是完全重复的)要查找需要保留的短
ID
s,请标记所有匹配项,以便跳过它们,然后查找以找到的匹配项结尾的所有
型号

创建按长度排序的
ID
s的
列表

var orderedIDs = testData.TestRecords.Select(tr => tr.ID).OrderBy(id => id.Length).ToList();
我认为没有任何方法可以有效地使用LINQ实现这一点,但是嵌套的
for
循环跳过以前的匹配可以优化对重复项的搜索

首先,跟踪
ID
s
的变量,以及哪些
ID已经匹配:

var dupRecordSubIDs = new List<string>();
var alreadyMatched = new bool[testData.TestRecords.Count];

基本上,您只想通过比较确定长字符串是否以短字符串结尾?如果是这样,您可以编写一个自定义的
IEqualityComparer
来进行比较。唯一的困难是尝试确定散列,但如果N有一个最小值,则可以对最后N个字符进行散列。如果输入值是
123
0123
,则结果应该是第一个、第二个,或者两者都是?@PiotrŁazarczyk结果应该返回一组模型,其中包含标记的重复ID的最后n个值。我可能知道,在我的第一个linq语句中对重复值的初始确定可能不准确,因为这是一个很难比较的结果。@juharr这是一个有趣的建议,但有一个问题正在进行的问题,我不能简单地分配一个已知的N值,我们可以为其设置子字符串。我忍不住觉得这是某种形式的边界代码改进/审阅风格的问题,因为您想使用LINQ而不是
foreach
。不确定这是否是关于SO的主题?有趣的是,thoughMatch需要表示一组模型,在这些模型中,我们比较它们的字段“ID”,它是一个字符串
var dupRecordSubIDs = new List<string>();
var alreadyMatched = new bool[testData.TestRecords.Count];
// foreach ID in length order
for (int n1 = 0; n1 < testData.TestRecords.Count-1; ++n1) {
    // skip the ones that already matched a shorter ID
    if (!alreadyMatched[n1]) {
        // remember if the shorter ID was alrady added
        var added_n1 = false;
        // compare the ID to all greater than or equal length IDs
        for (int n2 = n1 + 1; n2 < testData.TestRecords.Count; ++n2) {
            // if not previously matched, see if we have a new match
            if (!alreadyMatched[n2] && orderedIDs[n2].EndsWith(orderedIDs[n1])) {
                // only add the shorter ID once for new matches
                if (!added_n1) {
                    dupRecordSubIDs.Add(orderedIDs[n1]);
                    added_n1 = true;
                }
                // remember which longer IDs are already matched
                alreadyMatched[n2] = true;
            }
        }
    }
}
var modelRecords = testData.Models.Where(m => dupRecordSubIDs.Any(d => m.ID.EndsWith(d))).ToList();