在C#中跨多个列表查找公共项的最快方法

时间:2008-09-03 04:13:47

标签: c#

鉴于以下内容:

List<List<Option>> optionLists;

什么是确定所有N个列表中出现的Option对象子集的快速方法?等式是通过一些字符串属性确定的,例如option1.Value == option2.Value。

因此,我们最终应该使用List<Option>,其中每个项目只出现一次。

11 个答案:

答案 0 :(得分:8)

好的,这将找到出现在每个列表中的值的Option对象列表。

var x = from list in optionLists
        from option in list
        where optionLists.All(l => l.Any(o => o.Value == option.Value))
        orderby option.Value
        select option;

它不执行“distinct”选择,因此它将返回多个Option对象,其中一些具有相同的值。

答案 1 :(得分:3)

这是一个更有效的实现:

static SortedDictionary<T,bool>.KeyCollection FindCommon<T> (List<List<T>> items)
{
  SortedDictionary<T, bool>
    current_common = new SortedDictionary<T, bool> (),
    common = new SortedDictionary<T, bool> ();

  foreach (List<T> list in items)
  {
    if (current_common.Count == 0)
    {
      foreach (T item in list)
      {
        common [item] = true;
      }
    }
    else
    {
      foreach (T item in list)
      {
        if (current_common.ContainsKey(item))
          common[item] = true;
        else
          common[item] = false;
      }
    }

    if (common.Count == 0)
    {
      current_common.Clear ();
      break;
    }

    SortedDictionary<T, bool>
      swap = current_common;

    current_common = common;
    common = swap;
    common.Clear ();
  }

  return current_common.Keys;
}    

它的工作原理是创建一组迄今为止处理的所有列表共有的所有项目,并将每个列表与此集合进行比较,创建当前列表共有的临时项目集和目前常用项目列表。有效地为O(n.m),其中n是列表的数量,m是列表中的项目数。

使用它的一个例子:

static void Main (string [] args)
{
  Random
    random = new Random();

  List<List<int>>
    items = new List<List<int>>();

  for (int i = 0 ; i < 10 ; ++i)
  {
    List<int>
      list = new List<int> ();

    items.Add (list);

    for (int j = 0 ; j < 100 ; ++j)
    {
      list.Add (random.Next (70));
    }
  }

  SortedDictionary<int, bool>.KeyCollection
    common = FindCommon (items);

  foreach (List<int> list in items)
  {
    list.Sort ();
  }

  for (int i = 0 ; i < 100 ; ++i)
  {
    for (int j = 0 ; j < 10 ; ++j)
    {
      System.Diagnostics.Trace.Write (String.Format ("{0,-4:D} ", items [j] [i]));
    }

    System.Diagnostics.Trace.WriteLine ("");
  }

  foreach (int item in common)
  {
    System.Diagnostics.Trace.WriteLine (String.Format ("{0,-4:D} ", item));
  }
}

答案 2 :(得分:3)

Matt's answer的基础上,由于我们只对所有列表共有的选项感兴趣,我们只需检查第一个列表中其他人共享的选项:

var sharedOptions =
    from option in optionLists.First( ).Distinct( )
    where optionLists.Skip( 1 ).All( l => l.Contains( option ) )
    select option;

如果选项列表不能包含重复的entires,则不需要Distinct调用。如果列表的大小差异很大,最好迭代最短列表中的选项,而不是任何列表恰好是First。可以使用排序或散列集合来改善Contains调用的查找时间,但对于中等数量的项目,它不会有太大差异。

答案 3 :(得分:1)

使用 hashSet 怎么样?这样你就可以在O(n)中做你想做的事情,其中​​n是所有列表中的项目数量,我认为这是最快的方法。

你只需迭代每个列表并将找到的值插入到hashset中 当您插入已存在的密钥时,您将收到 false 作为.add method,的返回值,否则返回 true

答案 4 :(得分:1)

最快写:)

var subset = optionLists.Aggregate((x, y) => x.Intersect(y))

答案 5 :(得分:0)

排序,然后做类似于合并排序的事情。

基本上你会这样做:

  1. 从每个列表中检索第一项
  2. 比较项目,如果相等,则输出
  3. 如果任何项目在其他项目之前,则按顺序排序,从相应列表中检索新项目以替换它,否则,从所有列表中检索新项目以全部替换它们
  4. 只要你还有物品,请回到2。

答案 6 :(得分:0)

我没有性能统计信息,但是如果您不想滚动自己的方法,则各种集合库都有一个“Set”或“Set(T)”对象,它提供了常用的设置过程。 (按照我将使用的顺序列出)。

  1. IESI Collections(字面意思只是设置课程)
  2. PowerCollections(暂时没有更新)
  3. C5(从未亲自使用过)

答案 7 :(得分:0)

您可以通过计算所有列表中所有项目的出现次数来实现此目的 - 那些出现次数等于列表数量的项目对所有列表都是通用的:

    static List<T> FindCommon<T>(IEnumerable<List<T>> lists)
    {
        Dictionary<T, int> map = new Dictionary<T, int>();
        int listCount = 0; // number of lists

        foreach (IEnumerable<T> list in lists)
        {
            listCount++;
            foreach (T item in list)
            {
                // Item encountered, increment count
                int currCount;
                if (!map.TryGetValue(item, out currCount))
                    currCount = 0;

                currCount++;
                map[item] = currCount;
            }
        }

        List<T> result= new List<T>();
        foreach (KeyValuePair<T,int> kvp in map)
        {
            // Items whose occurrence count is equal to the number of lists are common to all the lists
            if (kvp.Value == listCount)
                result.Add(kvp.Key);
        }

        return result;
    }

答案 8 :(得分:0)

$http({
    method: 'POST',
    url: 'http://xxx.xx.xx.xxx:3000/login',
    data: self.user
}).then(function successCallback(response, status, headers, config) {
    console.log(response.headers('Set-Cookie'))  // logs null
    console.log(response.headers('set-cookie'))  // logs null
}, function errorCallback(response) {
    return response
});

答案 9 :(得分:0)

@Skizz方法不正确。它还返回项目中所有列表不常见的项目。 以下是更正后的方法:

/// <summary>.
    /// The method FindAllCommonItemsInAllTheLists, returns a HashSet that contains all the common items in the lists contained in the listOfLists,
    /// regardless of the order of the items in the various lists.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="listOfLists"></param>
    /// <returns></returns>
    public static HashSet<T> FindAllCommonItemsInAllTheLists<T>(List<List<T>> listOfLists)
    {
        if (listOfLists == null || listOfLists.Count == 0)
        {
            return null;
        }
        HashSet<T> currentCommon = new HashSet<T>();
        HashSet<T> common = new HashSet<T>();

        foreach (List<T> currentList in listOfLists)
        {
            if (currentCommon.Count == 0)
            {
                foreach (T item in currentList)
                {
                    common.Add(item);
                }
            }
            else
            {
                foreach (T item in currentList)
                {
                    if (currentCommon.Contains(item))
                    {
                        common.Add(item);
                    }
                }
            }
            if (common.Count == 0)
            {
                currentCommon.Clear();
                break;
            }
            currentCommon.Clear(); // Empty currentCommon for a new iteration.
            foreach (T item in common) /* Copy all the items contained in common to currentCommon. 
                                        *            currentCommon = common; 
                                        * does not work because thus currentCommon and common would point at the same object and 
                                        * the next statement: 
                                        *            common.Clear();
                                        * will also clear currentCommon.
                                        */
            {
                if (!currentCommon.Contains(item))
                {
                    currentCommon.Add(item);
                }
            }
            common.Clear();
        }

        return currentCommon;
    }

答案 10 :(得分:0)

在搜索了网络并且没有真正想出我喜欢的东西(或者说有效的东西)之后,我睡了一觉并想出了这个。我的SearchResult与您的Option类似。它中有一个EmployeeId,这就是我需要在列表中共同使用的东西。我返回每个列表中都有EmployeeId的所有记录。它并不华丽,但它简单易懂,只是我喜欢的东西。对于小名单(我的情况),它应该表现得很好 - 任何人都可以理解它!

private List<SearchResult> GetFinalSearchResults(IEnumerable<IEnumerable<SearchResult>> lists)
{
    Dictionary<int, SearchResult> oldList = new Dictionary<int, SearchResult>();
    Dictionary<int, SearchResult> newList = new Dictionary<int, SearchResult>();

    oldList = lists.First().ToDictionary(x => x.EmployeeId, x => x);

    foreach (List<SearchResult> list in lists.Skip(1))
    {
        foreach (SearchResult emp in list)
        {
            if (oldList.Keys.Contains(emp.EmployeeId))
            {
                newList.Add(emp.EmployeeId, emp);
            }
        }

        oldList = new Dictionary<int, SearchResult>(newList);
        newList.Clear();
    }

    return oldList.Values.ToList();
}