查找与所有其他项目相关的项目

时间:2019-03-29 19:05:58

标签: c# linq

我有以下课程:

public class Relation {
  public Int32 SourceId { get; set; }
  public Int32 TargetId { get; set; }
}

以及以下列表:

List<Relation> relations = service.GetRelations();

我需要选择与所有目标相关的SourceId。

因此,给出以下带有对(SourceId, TargetId)的示例:

(1, 1), (1, 2), (2, 1), (3, 2)

在这种情况下,TargetId可以是1或2。

与所有SourceId相关的唯一TargetIds (1, 2)SourceId 1

SourceId 2仅与TargetId 1相关,而SourceId 3仅与TargetId 2相关。

我该怎么做?

4 个答案:

答案 0 :(得分:1)

您需要收集所有可能的目标ID:

var input = new []
{
    new Relation(1, 1),
    new Relation(1, 2),
    new Relation(2, 1), 
    new Relation(3, 2), 
};

var allTargetId = input.Select(x => x.TargetId).Distinct().ToArray();

然后按来源ID进行分组,并在每个组中检查allTargetId中是否存在所有组成员:

var result = input.GroupBy(x => x.SourceId, x => x.TargetId)
    .Where(g => allTargetId.All(x => g.Contains(x)))
    .Select(g => g.Key)
    .ToArray();

注意:为使此代码正常工作,我向您的Relation类添加了一个构造函数,看起来像

public class Relation
{
    public Relation(int sourceId, int targetId)
    {
        SourceId = sourceId;
        TargetId = targetId;
    }

    public Int32 SourceId { get; set; }
    public Int32 TargetId { get; set; }
}


编辑

要获取Relation,可以使用以下查询:

var result = input.GroupBy(x => x.SourceId)
    .Where(g => allTargetId.All(x => g.Select(y => y.TargetId).Contains(x)))
    .SelectMany(g => g)
    .ToArray();

请注意,我仅使用linq2objects对其进行了测试,所以我不确定如何将其转换为SQL

答案 1 :(得分:0)

以下代码完成了您所要求的。它具有单元测试的形式,因此您可以检查不同的情况

        [Fact]
        public void FindSourcesThatTargetAll()
        {
            var list = new List<Relation>
            {
                new Relation(1, 1), new Relation(1, 2), new Relation(2, 1), new Relation(3, 2)

            };
            var allTargets = list.Select(x => x.TargetId).Distinct().OrderBy(x=>x).ToList();

            var dict = list.GroupBy(x => x.SourceId).ToDictionary(x => x.Key,
                grouping => grouping.Select(y => y.TargetId).Distinct().OrderBy(x=>x).ToList());

            var sourcesThatTargetAll = dict.Where(x => x.Value.Count == allTargets.Count).Select(x => x.Key).ToList();

            Assert.Single(sourcesThatTargetAll);
            Assert.Equal(1, sourcesThatTargetAll.First());

        }

我基本上做到了:

  1. 找到所有目标。
  2. 对于每个来源,找到所有目标(区别很重要),并按字典中的来源将其分组(dict变量)
  3. 从上面的词典中选择与所有目标匹配的所有源(示例中的数量已足够,但您可以进行更复杂的比较)

答案 2 :(得分:0)

一种简单的方法是将记录按*v.begin() = *(v.end() - 2)分组,然后找到所有TargetId的交集

SourceId

var groups = relations.GroupBy(r => r.TargetId).ToArray(); if (groups.Length > 0) { var set = new HashSet<int>(groups[0]); for (int i = 1; i < groups.Length; ++i) set.IntersectWith(groups[i].Select(r => r.SourceId)); } 的末尾将包含与所有set相关的所有SourceId

答案 3 :(得分:0)

    public class Relation
    {
        public Int32 SourceId { get; set; }
        public Int32 TargetId { get; set; }
    }

    public Int32?[] FindRelation(Relation[] relations)
    {
        List<Int32?> sourceIds = new List<int?>;
        var countOfTargets = relations.Select(x => x.TargetId).Distinct().Count();

        var relationsGroupedBySource = relations.GroupBy(x => x.SourceId);

        foreach (var group in relationsGroupedBySource)
        {
            var distinctGroup = group.Distinct();

            if (distinctGroup.Count() == countOfTargets)
            {
                sourceIds.Add(distinctGroup.Select(x => x.SourceId).First());
            }
        }

        return sourceIds.ToArray();
    }

  public void Test()
  {
        Relation[] relations = { 
                                   new Relation() { SourceId = 1, TargetId = 1 },
                                   new Relation() { SourceId = 1, TargetId = 2 },
                                   new Relation() { SourceId = 2, TargetId = 1 },
                                   new Relation() { SourceId = 3, TargetId = 2 }
                               };

     var sourceIds = FindRelation(relations);
  }