C#: Rename/replace duplicates in list with an added number - c#

I have a List<string> where I would want to replace all duplicates with an added number to them. An example would be:
{"Ply0", "Ply+45", "Ply-45", "Ply0"}
I would like each "Ply0" to have a unique name, so replace them with "Ply0_1" and "Ply0_2". It is important that the order of the list stays the same. Afterwards the list should look like this:
{"Ply0_1", "Ply+45", "Ply-45", "Ply0_2"}
I have tried first finding the duplicates with LINQ but I am new to it and also have trouble replacing them with the added number while keeping the order of the original list.
Any help would be greatly appreciated!

Using linq, it can be done like this, but i don't think it is much readable
var listx = new List<string>() { "Ply0", "Ply+45", "Ply-45", "Ply0" };
var res = listx.Select((s, i) => new { orgstr=s, index = i })
.GroupBy(x => x.orgstr)
.SelectMany(g => g.Select((x, j) => new { item = x, suffix = j + 1, count = g.Count() }))
.OrderBy(x => x.item.index)
.Select(x => x.count == 1 ? x.item.orgstr : x.item.orgstr + "_" + x.suffix)
.ToList();

Related

Compare and combine strings to get duplicates

I will try to describe my question in the best way I can.
I have a list with X strings ("NOTION", "CATION", "COIN", "NOON").
I am trying to compare them and find the most times each character (letter) was used, use that to get the number of that character, arrange them in alphabetical order, and create a string.
So the result string should be: "ACINNOOT"
Hope is clear what I am describing.
EDIT
So far:
for (int i = 0; i < currentWord.Length; i++)
{
string letter = word.Substring(i, 1);
tempDuplicatedLetterList.Add(letter);
}
// Which letters are repeated and how many times
var duplicatedQuery = tempDuplicatedLetterList.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => new { Element = y.Key, Counter = y.Count() })
.ToList();
I came to this, although I think there might be a cleaner way to do it:
var characterSets = new string[] { "NOTION", "CATION", "COIN", "NOON" }
.SelectMany(c => c.GroupBy(cc => cc)) // create character groups for each string, and flatten the groups
.GroupBy(c => c.Key) // group the groups
.OrderBy(cg => cg.Key) // order by the character (alphabetical)
.Select(cg => new string(cg.Key, cg.Max(v => v.Count()))) // create a string for each group, using the maximum count for that character
.ToArray(); // make an array
var result = string.Concat(characterSets);

List<T> extension method First, Second, Third....Nth

I want to access the first, second, third elements in a list. I can use built in .First() method for accessing first element.
My code is as follows:
Dictionary<int, Tuple<int, int>> pList = new Dictionary<int, Tuple<int, int>>();
var categoryGroups = pList.Values.GroupBy(t => t.Item1);
var highestCount = categoryGroups
.OrderByDescending(g => g.Count())
.Select(g => new { Category = g.Key, Count = g.Count() })
.First();
var 2ndHighestCount = categoryGroups
.OrderByDescending(g => g.Count())
.Select(g => new { Category = g.Key, Count = g.Count() })
.GetNth(1);
var 3rdHighestCount = categoryGroups
.OrderByDescending(g => g.Count())
.Select(g => new { Category = g.Key, Count = g.Count() })
.GetNth(2);
twObjClus.WriteLine("--------------------Cluster Label------------------");
twObjClus.WriteLine("\n");
twObjClus.WriteLine("Category:{0} Count:{1}",
highestCount.Category, highestCount.Count);
twObjClus.WriteLine("\n");
twObjClus.WriteLine("Category:{0} Count:{1}",
2ndHighestCount.Category, 2ndHighestCount.Count);
// Error here i.e. "Can't use 2ndHighestCount.Category here"
twObjClus.WriteLine("\n");
twObjClus.WriteLine("Category:{0} Count:{1}",
3rdHighestCount.Category, 3rdHighestCount.Count);
// Error here i.e. "Can't use 3rdHighestCount.Category here"
twObjClus.WriteLine("\n");
I have written extension method GetNth() as:
public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n)
{
if (n < 0)
throw new ArgumentOutOfRangeException("n");
if (n > 0){
int c = 0;
foreach (var e in list){
if (c % n == 0)
yield return e;
c++;
}
}
}
Can I write extension methods as .Second(), .Third() similar to
built in method .First() to access second and third indices?
If what you're looking for is a single object, you don't need to write it yourself, because a built-in method for that already exists.
foo.ElementAt(1)
will get you the second element, etc. It works similarly to First and returns a single object.
Your GetNth method seems to be returning every Nth element, instead of just the element at index N. I'm assuming that's not what you want since you said you wanted something similar to First.
Since #Eser gave up and doesn't want to post the correct way as an answer, here goes:
You should rather do the transforms once, collect the results into an array, and then get the three elements from that. The way you're doing it right now results in code duplication as well as grouping and ordering being done multiple times, which is inefficient.
var highestCounts = pList.Values
.GroupBy(t => t.Item1)
.OrderByDescending(g => g.Count())
.Select(g => new { Category = g.Key, Count = g.Count() })
.Take(3)
.ToArray();
// highestCounts[0] is the first count
// highestCounts[1] is the second
// highestCounts[2] is the third
// make sure to handle cases where there are less than 3 items!
As an FYI, if you some day need just the Nth value and not the top three, you can use .ElementAt to access values at an arbitrary index.

Sort my List of records by the most frequent values then the next most frequent

Sort my List of records so the records with the most in common values are displayed first.
I thought I found the solution in this link, but this only sorts by the first value.
My unsorted list look like this...
I would like my sorted list to look like this....
The Code I tired based on the above link...
lp = lp.GroupBy(x => x.Name)
.OrderByDescending(g => g.Count())
.SelectMany(g => g).ToList();
dataGridView2.DataSource = lp;
But as explained this only sorts by the first most common item.
Try this :
var result = lp.OrderBy(c => c.Value1).ThenBy(n => n.Value2).ThenBy(n => n.Value3)
If I've understood well... You can try this
lp.Select(t => new {
Thing = t, Coincidences = lp.Max(t2 =>
t2 != t ?
Convert.ToInt32(t2.Value1 == t.Value1) +
Convert.ToInt32(t2.Value2 == t.Value2) +
Convert.ToInt32(t2.Value3 == t.Value3) +
Convert.ToInt32(t2.Value4 == t.Value4)
: 0)
}).OrderByDescending(d => d.Coincidences).Select(d => d.Thing);

How to split a Guid List in to small part of lists

I have a Guid List type of varchar(Max). This list has lots of Guid's which cross the sql limit.SO i have break this list in to small list as shown below.
var sublists = customerList
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / 2000)
.Select(x => x.Select(v => v.Value.ToString()).ToList())
.ToArray();
But this list is coming in char format as shown below.
I am not getting why this is coming in char format. Am I making any mistakes?
If customerList is a big string:
var sublists = customerList
.Split(",")
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / 2000)
.Select(x => x.Select(v => v.Value).ToList())
.ToList();
Is the same solution as here but you must add Split Method first.
Try this:
string[] sublists = customerList.Substring(0,2000).Split(',');
sublists = sublists.Take(sublists.Length - 1).ToArray();
That should give you the results you are looking for.

Linq select strings from list when condition is met and save the index

I have a list of strings
List<string> lstOne = new List<string>() { "January:1", "February", "March:4"};
And I am filtering the strings that contain : with this code
var withcolumns = lstOne.Find(t => t.Contains(':'));
and I am getting a new list with { "January:1", "March:4"}
I want to select in a new list the values January:1 and March:4 but also save the indexes of then in the previous list so the result would be
"0" "January:1"
"2" "March:4"
I can be simple or complicated but right now my brain is not functioning to solve this issue.
list.Select((item, index) => new { item, index })
.Where(o => o.item.Contains(':'))
not sure what you want as the result ? a list of strings? or ?
but anyways.....with the index prefixed to your string...
List<string> lstOne = new List<string>() { "January:1", "February", "March:4" };
var list = lstOne.Select((s, i) => i+ " " + s ).Where(s => s.Contains(":")).ToList();

Categories