将连接值对分组到列表中

时间:2013-05-29 17:12:56

标签: c# list grouping

所以我正在研究一个问题,然后碰到一堵我似乎无法找到解决办法的墙。我从操作系统那里得到了很多信息,我想我会在这里问一下,看看有没有办法比我找到的更好。 基本上,我有一个类中包含一堆值,但出于我们的目的,只有一个重要。

   public class GroupPair
   {
       public string object1 { get; set; }
       public string object2 { get; set; }
       public List<string> BothObjects
       {
           get
           {
               List<string> s= new List<string>();
               s.Add(object1);
               s.Add(object2);
               return s;
           }
  }

我有一个List,我需要能够将它们分组。变得棘手的是两个值都不是唯一的,并且组的大小和组的数量是可变的。我基本上需要一种方式来说,“给我每个可以从这个列表组成的组,其中每个组包含所有包含该组的任何个体成员的对。”让我举几个例子......这里有一些对:

a   d
f   h
d   t
n   w
h   a
n   o
q   d
w   f
o   y

分组后,这就是我想要的:

Group 1
a   d
h   a
q   d
f   h
w   f
d   t

Group 2
n   x
n   o
o   y

融化你的大脑了吗? 关于如何做到这一点的任何想法,或者即使我可以自己研究这种概念的名称?

4 个答案:

答案 0 :(得分:1)

这是我快速而肮脏的方法。

简短说明:
我们的想法是从一对开始(可以将其视为图中的节点)。从该节点添加任何相邻节点(具有共享成员的对)。然后搜索刚刚添加的节点旁边的节点。您一直跟踪已访问过的节点,因此您无法循环播放。

public static List<HashSet<GroupPair>> GetGroups(IEnumerable<GroupPair> pairs)
{
   var groups = new List<HashSet<GroupPair>();

   var unassignedPairs = new HashSet<GroupPair>(pairs);
   while (unassignedPairs.Count != 0)
   {
      var group = new HashSet<GroupPair>();
      var rootPair = unassignedPairs.First();
      group.Add(rootPair);
      unassignedPairs.Remove(rootPair);

      var membersToVisit = new Queue<string>(rootPair.BothObjects);
      var visited = new HashSet<string>();
      while (members.Count != 0)
      {
         string member = membersToVisit.Dequeue();
         visited.Add(member);
         foreach (var newPair in unassignedPairs
                 .Where(p => p.BothObjects.Contains(member)).ToList())
         {
            group.Add(newPair);
            unAssignedPairs.Remove(newPair);
            foreach (var newMember in newPair.BothObjects.Except(visited))
            {
               membersToVisit.Enqueue(newMember)
            }
         }
      }
      groups.Add(group);
   }
   return groups;
}

答案 1 :(得分:0)

这只是一个解决方案的想法。

你需要知道你有多少独特的'个人'。对于你的例子,它是26。

首先,你创建了一个26对的字典,其中key是个体,在我们的例子中是一个字母,值是一个组号,它将在最后。对于每对,初始值应为零。

其次,保留一个'groupNumber'整数变量,用于存储下一个组号。你用1初始化它。

然后,您遍历GroupPairs列表。您获取第一个包含“a”和“d”的GroupPair,并将字典中的相应值设置为“1”。

对于每个后续的GroupPair,您将获取其个人并在字典中查找相应的值。

如果其中一个值非零,即其中一个人已经属于某个组,则将另一个值设置为相同的数字,从而将其置于同一组中。

如果两个值均为零,则将它们设置为“groupNumber”并递增“groupNumber”。

如果两个值都不为零,那么它就会变得有点棘手。您可以在组字典中找到所有对,其中value等于该对中的第二个值,并将其值设置为该对中的第一个值。

完成后,再次迭代GroupPairs列表。对于每一对,您在组词典中查找第一个人,从而找出该组所属的组。

希望有道理......

答案 2 :(得分:0)

此代码与示例输入匹配并生成所需的输出。基本上,我为每个组保留一个HashSet项目,并列出要处理的剩余项目。

private static void GroupPairs(List<Tuple<string, string>> pairs)
{
    int groupCounter = 0;

    while (pairs.Count > 0)
    {
        var onegroup = new HashSet<string>();
        Console.WriteLine("Group {0}", ++groupCounter);

        int initialGroupCount;
        do
        {
            var remainder = new List<Tuple<string, string>>();
            initialGroupCount = onegroup.Count;
            foreach (var curr in pairs)
            {
                if (onegroup.Contains(curr.Item1) ||
                    onegroup.Contains((curr.Item2)) ||
                    onegroup.Count == 0)
                {
                    Console.WriteLine("{0} {1}", curr.Item1, curr.Item2);
                    onegroup.Add(curr.Item1);
                    onegroup.Add(curr.Item2);
                }
                else
                {
                    remainder.Add(curr);
                }
            }
            pairs = remainder;
        } while (initialGroupCount < onegroup.Count);
    }
}

答案 3 :(得分:0)

为了完整起见,我还有一个递归解决方案。 接近结尾的是GroupPair类,它充当datacontainer,有两个辅助方法:Add和Merge。

你这样调用它:

var gp = GroupByPairs(
            new List<Tuple<string, string>>
                {
                    new Tuple<string, string>("a", "d"),
                    new Tuple<string, string>("f", "h"),
                    /*  you get the idea */
                }.GetEnumerator());

foreach (var groupData in gp)
{
    Console.WriteLine(groupData.ToString());
}

//recursive take on the problem
private static IEnumerable<GroupPair> GroupByPairs(
    IEnumerator<Tuple<string, string>> pairs)
{
    // result Groups
    var listGroup = new List<GroupPair>();
    if (pairs.MoveNext())
    {
        var item = pairs.Current;
        var current = new GroupPair(item);

        var subgroup = GroupByPairs(pairs); // recurse

        // loop over the groups
        GroupPair target = null;
        foreach (var groupData in subgroup)
        {
            // find the group the current item matches
            if (groupData.Keys.Contains(item.Item1) ||
                groupData.Keys.Contains(item.Item2))
            {
                // determine if we already have a target
                if (target == null)
                {
                    // add item and keep groupData
                    target = groupData;
                    groupData.Add(item);
                    listGroup.Add(groupData);
                }
                else
                {
                    // merge this with target
                    // do not keep groupData 
                    target.Merge(groupData);
                }
            }
            else
            {
                // keep groupData
                listGroup.Add(groupData);
            }
        }
        // current item not added
        // store its group in the listGroup
        if (target == null) 
        {
            listGroup.Add(current);    
        }
    }
    return listGroup;
}

public class GroupPair
{
    private static int _groupsCount = 0;
    private int id;

    public GroupPair(Tuple<string, string> item)
    {
        id = Interlocked.Increment(ref _groupsCount);
        Keys = new HashSet<string>();
        Items = new List<Tuple<string, string>>();
        Add(item);
    }

    // add the pair and update the Keys
    public void Add(Tuple<string, string> item)
    {
        Keys.Add(item.Item1);
        Keys.Add(item.Item2);
        Items.Add(item);
    }

    // Add all items from another GroupPair
    public void Merge(GroupPair groupPair)
    {
        foreach (var item in groupPair.Items)
        {
            Add(item);
        }
    }

    public HashSet<string> Keys { get; private set; }

    public List<Tuple<string, string>> Items { get; private set; }

    public override string ToString()
    {
        var build = new StringBuilder();
        build.AppendFormat("Group {0}", id);
        build.AppendLine();
        foreach (var pair in Items)
        {
            build.AppendFormat("{0} {1}", pair.Item1, pair.Item2);
            build.AppendLine();
        }
        return build.ToString();
    }
}