Reputation: 1669
I have a little algorithm issue and I'm stuck for a (fast) implementation. Actually mine is unfinished, but is already slowing down the load of DataGridView.
Initial problem: The DataGridView of WinForms has a bug from 2005 (apparently still unsolved up to VS2015) that makes incorrect bound of columns having the same name case in-sensitive. More precise if you have 2 columns "Cat" and "cat" they will bound both to the same (first found) object in database.
Anyhow, I am using ITypedList and GetItemProperties() to inform DGV for fields that I want to link. The idea (seen somewhere on stackoverflow) is to add spaces after name of "duplicate case in-sensitive" columns like this:
"cat" --> leave as is
"Cat" --> needs to be come "Cat_" _ means space
"cAt" --> needs to become "cAt__" __ means two spaces and so on
Algorithm problem: Add strings in a list with a loop. Before add, check if the string exists (trim and case insensitive) and if so, append a space to end of name. Leave the name unchanged as case. In other words, make strings unique by append n spaces to names.
Hope I described well, any idea appreciated.
I've done some tests with my variant and speed suffers, perhaps also do to the fact that DGV is firing GetItemProperties() callback 5 times or more.
Here is my code:
public partial class Form1 : Form
{
List<string> list = new List<string>
{
"cat", // the cat
"Cat", // first duplicate needs to become Cat_ (one space)
"kitty",
"kittY",
"dog",
"cAt", // third duplicate needs to become cAt__ (two spaces)
"Dog",
"monkey",
"monKey",
"Monkey",
};
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
AnimalHeaders phList = new AnimalHeaders();
for (int i = 0; i < list.Count(); i++)
{
string field = list.ElementAt(i);
var caseInsenList = phList.Where(z => z.Name.Trim().Equals(field, StringComparison.OrdinalIgnoreCase));
int count = caseInsenList.Count();
if (count == 0) // no results
{
phList.Add(new AnimalHeader { Name = field });
}
else // exists but can be many
{
for (j = 0; j < count; j++)
{
string found = caseInsenList.ElementAt(j).Name.Trim(); // no spaces
if (field == found)
continue; // exact match, case sensitive, we already have this, skip
else
{
}
}
}
}
}
}
public class AnimalHeader
{
public string Name { get; set; }
public Type Type { get; set; }
public int Order { get; set; }
}
public class AnimalHeaders : List<AnimalHeader>
{
}
Upvotes: 1
Views: 589
Reputation: 186668
Try a simple Linq: we are grouping same items and then add index
spaces (underscopes) to each index
th item within a group. Finally, we flatten (combine) all groups.
List<string> list = new List<string>() {
"cat", // the cat
"Cat", // first duplicate needs to become Cat_ (one space)
"kitty",
"kittY",
"dog",
"cAt", // third duplicate needs to become cAt__ (two spaces)
"Dog",
"monkey",
"monKey",
"Monkey",
};
List<string> result = list
.GroupBy(item => item, StringComparer.OrdinalIgnoreCase)
.SelectMany(chunk => chunk
.Select((item, index) => string.Format("{0}{1}", item, new string('_', index))))
.ToList();
Demo:
Console.WriteLine(string.Join(Environment.NewLine, result));
Outcome:
cat
Cat_
cAt__
kitty
kittY_
dog
Dog_
monkey
monKey_
Monkey__
Edit: In case we want to preserve the initial order we have to store it (index
of the item in the initial list) and finally order by it:
List<string> result = list
.Select((value, index) => new {
value,
index
})
.GroupBy(item => item.value, StringComparer.OrdinalIgnoreCase)
.SelectMany(chunk => chunk
.Select((item, index) => new {
value = string.Format("{0}{1}", item.value, new string('_', index)),
index = item.index
}))
.OrderBy(item => item.index)
.Select(item => item.value)
.ToList();
Outcome:
cat
Cat_
kitty
kittY_
dog
cAt__
Dog_
monkey
monKey_
Monkey__
Upvotes: 6