最有效的方法,使重复在集合中独一无二

时间:2014-01-24 21:26:12

标签: c# linq collections duplicates

我有一个收藏品。在此集合中,如果添加了副本,我想附加文本“ - N”(其中N是集合中当前项目未使用的整数)。

例如,如果我有以下列表:

  • ITEM1
  • ITEM2

并尝试再次添加'item1',我希望列表最终如此:

  • ITEM1
  • ITEM2
  • item1 - 1

如果我再次尝试添加“item1”,则列表将为:

  • ITEM1
  • ITEM2
  • item1 - 1
  • item1 - 2

非常直接。下面是我的简单算法,但在处理10,000个项目时,我的性能明显下降。显然这会发生一些,但有更好的方法吗?找不到任何类似的问题,所以我想看看是否有人遇到过类似的问题。

Item copyItem = new Item();
string tempName = name;
int copyNumber = 1;
while(copyItem != null)
{
    copyItem = MyCollection.FirstOrDefault(blah => blah.Name == tempName);
    if (copyItem == null)
    {
        name = tempName;
        break;
    }
    tempName = name + " - " + copyNumber;
    ++copyNumber;
}

3 个答案:

答案 0 :(得分:2)

我首先要对值进行排序 - 多亏了这一点,你只需要检查前一个值而不是整个集合。

所以看起来像这样:

        List<string> values = new List<string> { "item1", "item1", "item1" };

        values.Sort();

        string previousValue = string.Empty; 
        int number = 1; 
        for(int i = 0 ; i < values.Count; i ++) 
        {
            if (values[i].Equals(previousValue))
            {
                previousValue = values[i]; 
                values[i] = values[i] + "-" + number;
                number++;
            }
            else
            {
                previousValue = values[i]; 
                number = 1; 
            }

        }

答案 1 :(得分:2)

我会使用Dictionary<string, int>来存储特定项目的重复项数量。因此,辅助方法看起来像这样:

Dictionary<string, int> countDictionary = new Dictionary<string, int>(); // case sensitive!

string GetNameForItem(string itemName)
{
  var name = itemName;

  var count = 0;
  countDictionary.TryGetValue(itemName, out count);

  if (count > 0)
    name = string.Format("{0} - {1}", itemName, count);

  countDictionary[itemName] = count + 1;
  return name;
}

或者,如果您不希望GetNameForItem在检索时自动递增,则可以将操作拆分为多个方法:

int GetCountForItem(string itemName)
{
  var count = 0;
  countDictionary.TryGetValue(itemName, out count);

  return count;
}

string GetNameForItem(string itemName)
{
  var name = itemName;
  var count = GetCountForItem(itemName);

  if (count > 0)
    name = string.Format("{0} - {1}", itemName, count);

  return name;
}

int IncrementCountForItem(string itemName)
{
  var newCount = GetCountForItem(itemName) + 1;
  countDictionary[itemName] = newCount;

  return newCount;
}

重要的是要注意,如果您支持从集合中删除,则必须相应地更新计数:

int DecrementCountForItem(string itemName)
{
  var newCount = Math.Max(0, GetCountForItem(itemName) - 1); // Prevent count from going negative!
  countDictionary[itemName] = newCount;

  return newCount;
}

如果您有两个项目,例如“项目A ”和“项目A - 1 ”,您还必须记住会发生什么,然后删除“ 项目A “。您是否应该将“项目A - 1 ”重命名为“项目A ”?

答案 2 :(得分:0)

好吧所以你需要一个每个值的迭代器而不是一个全局迭代器。这段代码可以做到这一点。

        // Inputs for Tests purpose
        List<string> values = new List<string> { "item1", "item2", "item1", "item1" };
        // Result data
        List<string> finalResult = new List<string>();

        // 1 - Group by item value
        var tempResult = from i in values
                         group i by i;

        // We loop over all different item name
        foreach (var curItem in tempResult)
        {
            // Thanks to the group by we know how many item with the same name exists
            for (int ite = 0; ite < curItem.Count(); ite++)
            {
                if (ite == 0)
                    finalResult.Add(curItem.Key);
                else
                    finalResult.Add(string.Format("{0} - {1}", curItem.Key, ite));
            }
        }

感谢LINQ,您可以减少代码量,下一代码将执行完全相同的操作,并且应该更快,因为我使用ToList()方法,因此LINQ查询将不会有延迟执行。

         // Inputs for Tests purpose
        List<string> values = new List<string> { "item1", "item2", "item1", "item1" };
        // Result data
        List<string> finalResult = new List<string>();

        values.GroupBy<string, string>(s1 => s1).ToList().ForEach(curItem =>
        {
            for (int ite = 0; ite < curItem.Count(); ite++)
            {
                finalResult.Add(ite == 0 ? curItem.Key : string.Format("{0} - {1}", curItem.Key, ite));
            }
        });