使用Linq检索查找(或部分键)的值的替代方法

6
我正在处理以下代码,基于用户指定的搜索短语查找“描述”。将关键词视为重要短语,将值视为描述性信息来查找该短语。
可以将其视为商店定位器(我能想到的最佳类比)。如果您搜索“Target”(键),则会得到许多城市(值),其中一些可能具有相同的名称。因此,同一城市可能会有多个Target。
明显不能使用Dictionary,因为可能会有重复的商店名称或城市名称。这就是我的实际情况:
基本上,我从List<KeyValuePair<string, string>>开始,允许在两个方向上出现重复项,然后将其转换为Lookup<string, string>,但我认为它并没有像我想象的那样不易混淆。
List<KeyValuePair<string, string>> kvpList = new List<KeyValuePair<string, string>>();
Lookup<string, string> collection;

kvpList.Add(new KeyValuePair<string, string>("K1", "R1"));
kvpList.Add(new KeyValuePair<string, string>("K1", "R1"));
kvpList.Add(new KeyValuePair<string, string>("K1", "R2"));
kvpList.Add(new KeyValuePair<string, string>("K2", "R1"));
kvpList.Add(new KeyValuePair<string, string>("K2", "R2"));
kvpList.Add(new KeyValuePair<string, string>("K2", "R3"));
kvpList.Add(new KeyValuePair<string, string>("K2", "R1"));

collection = (Lookup<string,string>)kvpList.ToLookup(k => k.Key, k => k.Value);

上述仅为虚假测试信息,但我认为必须有更清洁的方法来获取“查找”功能的结果,因为它似乎非常适用于Linq。不幸的是,我对Linq非常陌生,而Linq本身的语法似乎并不容易理解。在这段代码中,我使用以下内容获取结果(硬编码搜索项仅供测试):
string searchTerm = "K2";
List<string> uniqueResults = new List<string>();

foreach (var item in collection)
{
    if (item.Key.Contains(searchTerm))
    {
        foreach (var value in item)
        {
            if (!uniqueResults.Contains(value))
            {
                uniqueResults.Add(value);
                Console.WriteLine("Added: " + value);
            }
            else
            {
                Console.WriteLine("Skipped duplicate: " + value);
            }
        }
    }
}  

我对以上代码没有问题,但我的问题是:是否有一种使用Linq来实现我想要完成的目标的方法?我感觉我所写的不是最好的方式...值得注意的是,部分的searchTerm也必须能够找到它出现在键中的结果(因此包含)。现有的答案无法完全回答我的特定问题。我找不到一种通过部分键搜索来获取值的方法。

查找类似于字典,没有子字符串搜索。如果需要子字符串搜索,则无法使用基于集合的方法,因为它们使用GetHashCode,无法处理字符串的一部分(如何为所有子字符串返回单个值?)。 - Tim Schmelter
我建议在你的问题中更加突出地提到“部分搜索词也必须能够找到它在关键字中出现的结果”,并将其作为标题和前几段的一部分。我认为这会很大程度上影响问题的形式。 - 31eee384
谢谢 @31eee384,已经进行了更改。 - Broots Waymb
2个回答

6
您的代码转换为LINQ后将是这样的:
var uniqueResults = collection
        .Where(item => item.Key.Contains(searchTerm)) // filter the collection
        .SelectMany(x => x)                           // flatten results
        .Distinct()                                   // remove duplicates
        .ToList();

你甚至不需要使用Lookup。你可以通过kvpList得到相同的结果:

var uniqueResults = kvpList
        .Where(item => item.Key.Contains(searchTerm)) // filter the collection
        .Select(item => item.Value)                   // get the Values from KeyValuePairs
        .Distinct()                                   // remove duplicates
        .ToList();

LINQ解决方案比命令式解决方案好理解得多。试着用英语描述算法: 从 kvpList 中选择包含搜索词的唯一值。这几乎就是第二个LINQ代码。

3
这里查找是无用的。建议使用只有kvpList(将collection替换为kvpList)。 - Tim Schmelter
@TimSchmelter 你说得对,这个版本比查找版本更容易理解。我在答案中包含了两个版本。 - Jakub Lortz
谢谢@JakubLortz!这让理解使用LINQ的方法变得更容易了。有上下文的情况下肯定更容易理解。 - Broots Waymb
说实话,当我尝试找到这个解决方案时,我认为Select和SelectMany比其他任何东西都更让我困扰。 - Broots Waymb

1
您可以使用LINQ和一个HashSet<string>,这将为您消除重复项:
var uniqueResults = collection.Where(item => item.Contains(searchTerm))
                              .SelectMany(x => x)
                              .ToHashSet();

其中ToHashSet是一个自定义的扩展方法,您可以轻松创建:

public static class EnumerableExtensions
{
    public static HashSet<T> ToHashSet(this IEnumerable<T> enumerable)
    {
        return new HashSet<T>(enumerable);
    }
}

你缺少一层展平。问题中的uniqueResults包含值,而不是HashSet的可枚举对象。 - CoderDennis
@CoderDennis 谢谢,我漏掉了。 - Yuval Itzchakov
1
不像Jakub的回答那么简单,但仍然非常酷!我给你点赞。 - Broots Waymb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接