1

I have this function, which works and gives the correct result:

  <System.Runtime.CompilerServices.Extension()>
  Public Function Unique(List As List(Of String)) As List(Of String)
    Return List.Select(Function(x, index) x & System.Text.RegularExpressions.Regex.Replace((List.GetRange(0, index).Where(Function(y) y = x).Count + 1).ToString, "\b1\b", String.Empty)).ToList
  End Function

This function appends a "2", "3", etc as needed, to those items that are not unique, to make them unique.

How can I remove the regex while a) staying in the same linq statement (the same line of code), b) without introducing a loop or c) evaluating the expression twice, as would be needed in an IIF statement?

This is not a duplicate of Getting index of duplicate items in a list in c#, because a) my list does not change during the function and b) that question was not answered with a ready to apply code example, and here I'm looking for a specific fix to a specific line of code. Those answers will not fix my issue; they do not apply here.

Community
  • 1
  • 1
toddmo
  • 20,682
  • 14
  • 97
  • 107
  • Do you care about preserving the order or could you use `GroupBy` to collect up all the ones with the same identifier and then add an index to each within a group? – Ian Mercer Nov 01 '14 at 03:14
  • Yes the order matters. These are the headers in a csv files and they have to match the row values. Thanks – toddmo Nov 01 '14 at 23:23

2 Answers2

1

You could do it using GroupBy and if you want to preserve the original order you can create an anonymous type to include it, then group, then re-sort by the original order.

    string[] input = new[]{ "Apple", "Orange", "Apple", "Pear", "Banana", 
                            "Apple", "Apple", "Orange" };

    var result = input
        // Remember the initial order
        .Select((name, i) => new {name, i})
        .GroupBy(x => x.name)
        // Now label the entries in each group
        .SelectMany(g => g.Select((item, j) => 
            new {i = item.i, name = (j == 0 ? item.name : item.name + (j+1))}))
        // Now reorder them by their original order
        .OrderBy(x => x.i)
        // Remove the order value to get back to just the name
        .Select(x => x.name)
        .ToList();


    foreach (var r in result)
        Console.WriteLine(r);

Result

Apple
Orange
Apple2
Pear
Banana
Apple3
Apple4
Orange2
Ian Mercer
  • 38,490
  • 8
  • 97
  • 133
0

Here's the VB version:

  <System.Runtime.CompilerServices.Extension()>
  Public Function Unique(List As List(Of String)) As List(Of String)
    ' 1. Remember the initial order
    ' 2. Group by the text
    ' 3. Label the entries in each group
    ' 4. Now reorder them by their original order
    ' 5. Remove the order value to get back to just the name
    Return List.
      Select(Function(Item, Index) New With {Item, Index}).
      GroupBy(Function(IndexedItem) IndexedItem.Item).
      SelectMany(Function(Group) Group.Select(Function(GroupItem, GroupIndex) New With {.Index = GroupItem.Index, .UniqueItem = GroupItem.Item & If(GroupIndex = 0, String.Empty, (GroupIndex + 1).ToString)})).
      OrderBy(Function(IndexedUniqueItem) IndexedUniqueItem.Index).
      Select(Function(IndexedUniqueItem) IndexedUniqueItem.UniqueItem).
      ToList()
  End Function
toddmo
  • 20,682
  • 14
  • 97
  • 107