如何根据“is-subset”条件有效地修剪列表?

时间:2011-09-28 10:29:09

标签: c# .net linq

我们假设给予Dictionary<int, List<int>>并且我想使用以下条件修剪它

  • 如果字典中存在itemitemLarger != itemitem.Value.Union(new[] { item.Key })
  • 的子集,则应从字典中删除itemLarger.Value.Union(new[] { itemLarger.Key })

也就是说,字典中的每个项目都将通过将项目的键附加到项目的值而获得的数字列表来表示,并且我想要删除那些由其他项目的表示的子集表示的项目。

示例:

var testResult = new Dictionary<int, List<int>>
{                
    { 2, new[] { 3, 4 }},
    { 3, new[] { 2, 4 }},
    { 1, new[] { 2, 3, 4 }},
    { 4, new[] { 2, 3 }}
};

在这种情况下,列表中剩下的唯一元素是{1, {2, 3, 4}}

我似乎无法找到一些优雅的方法,因为

  • GroupBy不允许我指定哪个元素应该被用作关键字当我有两个应该被分组时
  • Distinct不允许我指定,如果两个元素不相同,哪些元素应保留在列表中

当然,它可以通过微不足道的方式实现。我想知道是否有一个不错的。

感谢您的任何想法。

3 个答案:

答案 0 :(得分:2)

我不认为这与你所说的“琐碎”方式有很大的不同,但这是一个LINQ解决方案:

var sets = testResult
.Select(x => new { Key = x.Key, Set = new HashSet<int>(x.Value.Concat(new[] { x.Key })) })
.ToList();
var res = sets.Where(s => sets.Any(x => x.Set.IsSupersetOf(s.Set) && x.Key != s.Key));
var keysToRemove = res.Select(x => x.Key);

答案 1 :(得分:0)

在我的解决方案中,我检查testResult中的每个元素x,如果此元素是testResult中任何其他元素的子集。如果不是,则此元素在linq表达式中传递'where'过滤器。最后两行是将结果从列表表示转换为字典表示。

    var listResult = 
        (from x in testResult 
        where (from y in testResult 
               where !x.Value.Except(y.Value).Any() && x.Key != y.Key 
               select y).Count() == 0 
        select x).ToList();

    var dictionaryResult = new Dictionary<int, List<int>>();
    listResult.ForEach(x => dictionaryResult.Add(x.Key, x.Value));

编辑: 我们可以写得更短:

testResult = testResult.Where(x => 
  (from y in testResult 
   where !x.Value.Except(y.Value).Any() && x.Key != y.Key 
   select y).Count() == 0).ToDictionary(x => x.Key, x => x.Value);

答案 2 :(得分:0)

这可能不是最有效的方式,但它很简短且可读。

var test = new Dictionary<int, List<int>>
{                
    { 2, new List<int> { 3, 4 }},
    { 3, new List<int> { 2, 4 }},
    { 1, new List<int> { 2, 3, 4 }},
    { 4, new List<int> { 2, 3 }}
};

var res = test.Where(n => !test.Any(m => m.Key!=n.Key && n.Value.Intersect(m.Value).Count()==n.Value.Count) );