Please have a look in the below code, I need an output in OrderedListDesc = {7,6,5,4,1,2,3,8,9} instead of {4,5,6,7,1,2,3,8,9}.
List<long> List = new List<long>() { 1,2,4,5,3,8,6,7,9 };
List<long> ListAsc = new List<long>() { 4,5,6,7 };
List<long> ListDesc = new List<long>() { 7,6,5,4 };
var OrderedListAsc = List.OrderBy(b => ListAsc.FindIndex(a => a == b)).ToList();
foreach (var l in OrderedListAsc)
{
Console.Write(l+" ,");
}
Console.WriteLine();
var OrderedListDesc = List.OrderByDescending(b => ListDesc.FindIndex(a => a == b)).ToList();
foreach (var l in OrderedListDesc)
{
Console.Write(l + " ,");
}
It is really simple if you think about it:
The order of the elements found in ListDesc should be the number itself, then you got your result:
var OrderedListDesc = List.OrderByDescending(b => ListDesc.Any(a => a == b) ? b : 0).ToList();
foreach (var l in OrderedListDesc)
{
Console.Write(l + " ,");
}
If you want to see what's happening, that is, why you're getting things in the wrong order, run this:
foreach (var i in List)
{
Console.WriteLine("{0}, {1}", i, ListDesc.FindIndex(a => a == i));
}
There's no need for ListDesc anyway. Just use ListAsc:
var OrderedListDesc = List.OrderByDescending(b => ListAsc.FindIndex(a => a == b)).ToList();
Or, use ListDesc and call OrderBy rather than OrderByDescending:
var OrderedListDesc = List.OrderBy(b => ListDesc.FindIndex(a => a == b)).ToList();
If you notice the problem is, when an element(value) not found FindIndex returns -1, which will appear first in order. Assign the maximum value when element is not found.
var OrderedListDesc = List.OrderBy(b =>
{
var index = ListDesc.FindIndex(a => a == b);
return index==-1? int.MaxValue : index;
}).ToList();
A small tip (not relating to issue), if you want to print , separated values you could simply use string.Join as below.
Console.WriteLine(string.Join(",", OrderedListDesc));
Output:
7 ,6 ,5 ,4 ,1 ,2 ,3 ,8 ,9 ,
Check this Fiddle
Related
I have List of names like:
var list = new List<string> {"Allan", "Michael", "Jhon", "Smith", "George", "Jhon"};
and a combobox which itemssource is my list. As you can see in the list there is Jhon 2 times, what I want is when I put those name into combobox add "2" to second Jhon. I mean when I open the combobox names in it shoud look like:
Allan
Michael
Jhon
Smith
George
Jhon2
I have tired linq to do that but I'm quite new to c#/linq. Could someone show me simple way to do that?
I would do this:
var result = list.Take(1).ToList();
for (var i = 1; i < list.Count; i++)
{
var name = list[i];
var count = list.Take(i - 1).Where(n => n == name).Count() + 1;
result.Add(count < 2 ? name : name + count.ToString());
}
Here is what I would do:
First off, separate the list into two smaller ones, one that contains all the unique names, and one that contains only duplicates:
var duplicates = myList.GroupBy(s => s)
.SelectMany(grp => grp.Skip(1));
var unique = new HashSet<string>(myList).ToList();
Then process:
var result = new List<string>();
foreach (string uniqueName in unique)
{
int index=2;
foreach (string duplicateName in duplicates.Where(dupe => dupe == uniqueName))
{
result.Add(string.Format("{0}{1}", duplicateName, index.ToString()));
index++;
}
}
What we are doing here is the following:
Iterate through unique names.
Initialize a variable index with value 2. This will be the number we add at the end of each name.
Iterate through matching duplicate names.
Modify the name string by adding the number stored at index to the end.
Add this new value to the results list.
Increment index.
Finally, add the unique names back in:
result.AddRange(unique);
The result list should now contain all the same values as the original myList, only difference being that all names that appear more than once have a number appended to their end. Per your specification, there is no name name1. Instead, counting starts from 2.
Another possibility:
var groups = list.Select((name, index) => new { name, index }).GroupBy(s => s.name).ToList();
foreach (var group in groups.Where(g => g.Count() > 1))
{
foreach (var entry in group.Skip(1).Select((g, i) => new { g, i }))
{
list[entry.g.index] = list[entry.g.index] + entry.i;
}
}
Someone might be able to give a more efficient answer, but this does the job.
The dictionary keeps track of how many times a name has been repeated in the list. Each time a new name in the list is encountered, it is added to the dictionary and is added as is to the new list. If the name already exists in the dictionary (with the key check), instead, the count is increased by one in the dictionary and this name is added to the new list with the count (from the dictionary value corresponding to the name as the key) appended to the end of the name.
var list = new List<string> {"Allan", "Michael", "Jhon", "Smith", "George", "Jhon", "George", "George"};
Dictionary<string, int> dictionary = new Dictionary<string,int>();
var newList = new List<string>();
for(int i=0; i<list.Count();i++){
if(!dictionary.ContainsKey(list[i])){
dictionary.Add(list[i], 1);
newList.Add(list[i]);
}
else{
dictionary[list[i]] += 1;
newList.Add(list[i] + dictionary[list[i]]);
}
}
for(int i=0; i<newList.Count(); i++){
Console.WriteLine(newList[i]);
}
Output:
Allan
Michael
Jhon
Smith
George
Jhon2
George2
George3
Check this solution:
public List<string> AddName(IEnumerable<string> list, string name)
{
var suffixSelector = new Regex("^(?<name>[A-Za-z]+)(?<suffix>\\d?)$",
RegexOptions.Singleline);
var namesMap = list.Select(n => suffixSelector.Match(n))
.Select(x => new {name = x.Groups["name"].Value, suffix = x.Groups["suffix"].Value})
.GroupBy(x => x.name)
.ToDictionary(x => x.Key, x => x.Count());
if (namesMap.ContainsKey(name))
namesMap[name] = namesMap[name] + 1;
return namesMap.Select(x => x.Key).Concat(
namesMap.Where(x => x.Value > 1)
.SelectMany(x => Enumerable.Range(2, x.Value - 1)
.Select(i => $"{x.Key}{i}"))).ToList();
}
It handle case when you already has 'Jhon2' in the list
I would do
class Program
{
private static void Main(string[] args)
{
var list = new List<string> { "Allan", "Michael", "Jhon", "Smith", "George", "Jhon" };
var duplicates = list.GroupBy(x => x).Select(r => GetTuple(r.Key, r.Count()))
.Where(x => x.Count > 1)
.Select(c => { c.Count = 1; return c; }).ToList();
var result = list.Select(v =>
{
var val = duplicates.FirstOrDefault(x => x.Name == v);
if (val != null)
{
if (val.Count != 1)
{
v = v + " " + val.Count;
}
val.Count += 1;
}
return v;
}).ToList();
Console.ReadLine();
}
private static FooBar GetTuple(string key, int count)
{
return new FooBar(key, count);
}
}
public class FooBar
{
public int Count { get; set; }
public string Name { get; set; }
public FooBar(string name, int count)
{
Count = count;
Name = name;
}
}
I have a list of data which contains of random data with combination of string and number:
List<String> Data1 = new List<String>()
{
"1001A",
"1002A",
"1003A",
"1004A",
"1015A",
"1016A",
"1007A",
"1008A",
"1009A",
};
I want this data to arrange into series like this:
1001A - 1004A, 1007A - 1009A, 1015A, 1016A
for every more than 2 counts of data series the output shall be have "-" between the first count and the last count of series, the other non series data will be just added to the last part and all together will separated by ",".
I'd already made some codes only to arrange the data series by the last char of it:
string get_REVISIONMARK = "A";
var raw_serries = arrange_REVISIONSERIES.Where(p => p[p.Length - 1].ToString() == get_REVISIONMARK) .OrderBy(p => p[p.Length - 1) .ThenBy(p => p.Substring(0, p.Length - 1)).ToList();
just ignore the last char I'd already have function for that, and my problem only about the arrangement of the numbers, the length of data is not fixed. for other example of output "1001A - 1005A, 301A, 32A"
I had another sample of my codes this works fine to me, but for me its so lazy code.
for (int c1 = 0; c1 < list_num.Count; c1++)
{
if (list_num[c1] != 0)
{
check1 = list_num[c1];
for (int c2 = 0; c2 < list_num.Count; c2++)
{
if (check1 == list_num[c2])
{
list_num[c2] = 0;
check1 += 1;
list_series.Add(arrange_REVISIONSERIES[c2]);
}
}
check1 = 0;
if (list_series.Count > 2)
{
res_series.Add(list_series[0] + " to " +list_series[list_series.Count - 1]);
list_series.Clear();
}
else
{
if (list_series.Count == 1)
{
res_series.Add(list_series[0]);
list_series.Clear();
}
else
{
res_series.Add(list_series[0] + "," + list_series[1]);
list_series.Clear();
}
}
}
}
var combine_res = String.Join(",", res_series);
MessageBox.Show(combine_res);
this codes work fine for the series number ...
A possible solution (working with current set of values), Please follow the steps below
Declare a class level string list as
public List<String> data_result = new List<string>();
Create a function to iterate through input string list (input string declared inside, named 'data')
public void ArrangeList()
{
List<String> data = new List<string>() { "1001A", "1002A", "1003A",
"1004A", "1015A", "1016A", "1007A", "1008A", "1009A", "1017A" };
List<int> data_int = data.Select(a => Convert.ToInt32(a.Substring(0,
a.Length - 1))).OrderBy(b => b).ToList();
int initializer = 0, counter = 0;
int finalizer = 0;
foreach (var item in data_int)
{
if (initializer == 0)
{ initializer = item; continue; }
else
{
counter++;
if (item == initializer + counter)
finalizer = item;
else
{
LogListing(initializer, finalizer);
initializer = item;
finalizer = item;
counter = 0;
}
}
}
LogListing(initializer, finalizer);
}
Create a function which just logs the result into data_result string list.
public void LogListing(int initializer, int finalizer)
{
if (initializer != finalizer)
{
if (finalizer == initializer + 1)
{
data_result.Add(initializer + "A");
data_result.Add(finalizer + "A");
}
else
data_result.Add(initializer + "A - " + finalizer + "A");
}
else
data_result.Add(initializer + "A");
}
It perfectly generates the result list as
Thumb-up if you like
A linqy solution:
char get_REVISIONMARK = 'A';
var res = arrange_REVISIONSERIES.Select(s => new { Rev = s[s.Length - 1], Value = int.Parse(s.Substring(0, s.Length - 1)), Org = s })
.Where(d => d.Rev == get_REVISIONMARK).OrderBy(d => d.Value)
.Select((val, ind) => new { Index = ind, Org = val.Org, Value = val.Value }).GroupBy(a => a.Value - a.Index)
.Select(gr=>gr.ToList()).OrderBy(l=>l.Count > 2 ? 0 : 1 ).Aggregate(new List<string>(), (list, sublist) =>
{
if (sublist.Count > 2)
list.Add(sublist[0].Org + " - " + sublist[sublist.Count - 1].Org);
else
list.AddRange(sublist.Select(a => a.Org));
return list;
});
The first lines are basically the same as the code you already have (filter on revision and sort), but with the difference that the subvalues are stored in an anonymous type. You could do the same on the pre ordered list, but since splitting the string would be done twice I've included it in the total.
Then a select with index (.Select((val, ind) =>) is made to get value/index pairs. This is done to be able to get the sequences based on an old t-sql row_number trick: for each 'group' the difference between value and index is the same .GroupBy(a => a.Value - a.Index)
After that, normally you'd be as good as done, but since you only want to make sequences of 2 and longer, we make sublists out of the groupby values and do the ordering beforehand to make sure the ranges come for the eventual single elements .Select(gr=>gr.ToList()).OrderBy(l=>l.Count > 2 ? 0 : 1 )
Finally, the list is created of the groups. Several options, but I like to use Aggregate for that. The seed is the resulting list, and the aggregate simply adds to that (where subranges > 2 are cummulated and for single elements and pairs, the single elements are added)
I'm making two assumptions:
The list is already ordered
The non-numeric characters can be ignored
You will get the results in the results variable:
void Main()
{
List<String> Data1 = new List<String>()
{
"1001A",
"1002A",
"1003A",
"1004A",
"1015A",
"1016A",
"1007A",
"1008A",
"1009A",
};
var accu = new List<List<Tuple<int, string>>>();
foreach (var data in Data1)
{
if (accu.Any(t => t.Any(d => d.Item1 == (ToInt(data) - 1))))
{
var item = accu.First(t => t.Any(d => d.Item1 == (ToInt(data) - 1)));
item.Add(new Tuple<int, string>(ToInt(data), data));
}
else
{
accu.Add(new List<Tuple<int, string>>{ new Tuple <int, string>(ToInt(data), data)});
}
}
var results = new List<string>();
results.AddRange(accu.Where(g => g.Count > 2).Select(g => string.Format("{0} - {1}", g.First().Item2, g.Last().Item2)));
results.AddRange(accu.Where(g => g.Count <= 2).Aggregate(new List<string>(), (total, current) => { total.AddRange(current.Select(i => i.Item2)); return total; } ));
}
private static Regex digitsOnly = new Regex(#"[^\d]");
public static int ToInt(string literal)
{
int i;
int.TryParse(digitsOnly.Replace(literal, ""), out i);
return i;
}
So given your starting data:
List<String> arrange_REVISIONSERIES = new List<String>()
{
"1001A",
"1002A",
"1003A",
"1004A",
"1015A",
"1016A",
"1007A",
"1008A",
"1009A",
};
I do this first:
var splits =
arrange_REVISIONSERIES
.Select(datum => new
{
value = int.Parse(datum.Substring(0, datum.Length - 1)),
suffix = datum.Substring(datum.Length - 1, 1),
})
.OrderBy(split => split.suffix)
.ThenBy(split => split.value)
.ToArray();
That's basically the same as your raw_serries, but orders the number part as a number. It seems to me that you need it as a number to make the range part work.
I then do this to compute the groupings:
var results =
splits
.Skip(1)
.Aggregate(
new[]
{
new
{
start = splits[0].value,
end = splits[0].value,
suffix = splits[0].suffix
}
}.ToList(),
(a, s) =>
{
if (a.Last().suffix == s.suffix && a.Last().end + 1 == s.value)
{
a[a.Count - 1] = new
{
start = a.Last().start,
end = s.value,
suffix = s.suffix
};
}
else
{
a.Add(new
{
start = s.value,
end = s.value,
suffix = s.suffix
});
}
return a;
})
.Select(r => r.start == r.end
? String.Format("{0}{1}", r.end, r.suffix)
: (r.start + 1 == r.end
? String.Format("{0}{2}, {1}{2}", r.start, r.end, r.suffix)
: String.Format("{0}{2} - {1}{2}", r.start, r.end, r.suffix)))
.ToArray();
And finally, this to create a single string:
var result = String.Join(", ", results);
That gives me:
1001A - 1004A, 1007A - 1009A, 1015A, 1016A
This code nicely works with data containing different suffixes.
I have a list containing integer or string-integer
like this
TagNo FTerminal
1000 1
1000 5
1000 2S6
how can i get the result like this
TagNo FTerminal
1000 1
5
6
I have this , but definately it gives me error on 2s6.
how can i change it to cover all?
var terminalList = sourceLists.Where(t => t.TagNo == tagList)
.Where(t=>t.FromTerminal.Length>0)
.Select(t => int.Parse(t.FromTerminal))
.OrderBy(t=>t)
.ToList();
Instead of using int.Parse in your LINQ statement, you need to write your own function.
Something like this:
int parseTerminal(string input) {
int result = -1;
if (!int.TryParse(input, out result)) {
result = -99;
}
return result;
}
That would make your LINQ to
var terminalList = sourceLists
.Where( t => t.TagNo == tagList && t.FromTerminal.Length > 0 )
.Select( t => parseTerminak(t.FromTerminal) )
.OrderBy( t=>t )
.ToList();
Result:
TagNo FTerminal
1000 -99
1
5
You need to handle the special case where FromTerminal is not a number yourself.
A naive implementation of the requirement one could think of is something like this:
int parseTerminal(string input) {
int result = -1;
if (!int.TryParse(input, out result)) {
var temporaryString = string.Empty;
var lastInt = -1;
input.ToList().ForEach( aChar => {
if ( aChar >= '0' && aChar <= '9' ) {
temporaryString += aChar;
} else {
if ( temporaryString.Length >= 0 ) {
int.TryParse( temporaryString, out lastInt );
temporaryString = string.Empty;
}
}
} );
if ( temporaryString.Length >= 0 ) {
if (!int.TryParse( temporaryString, out lastInt )) {
lastInt = -98;
}
}
result = lastInt;
}
return result;
}
Note: I would not consider this production ready and you should think about edge cases.
Without knowing much about your data structure I have written a code using some system types.
var tuples = new List<Tuple<int, string>>
{
new Tuple<int, string>(1000, "1"),
new Tuple<int, string>(1000, "5"),
new Tuple<int, string>(1000,"2s6")
};
var enumerable = tuples.GroupBy(t => t.Item1).
Select(g => new Tuple<int, List<int>>(g.Key, g.Select(e => int.Parse(Regex.Match(e.Item2, #"(?<=(\D|^))\d+(?=\D*$)").Value)).ToList()));
The error occurs because you try to handle the string '2S6' as an integer, using int.Parse. This would naturally cause an exception.
I would suggest following another approach, using regular expressions. I think regular expressions are better solution since the data you ask come after string manipulation of the already retrieved query results.
Using regular expressions to do this kind of staff, would make it also easier for you to maintain in the future. Think of the case, that in a week you don't want to retrieve the last digit of the string, but the second digit of the string.
You can use this online regular expression tester to test your regular expression. I suppose the regular expression \d(?!.*\d) would be a good choice, since it returns the last digit.
This article is a good guide in using regular expressions in .NET, including examples.
Hope I helped!
if last symbol always is int you may change your code like this
var terminalList = sourceLists.Where(t => t.TagNo == tagList)
.Where(t=>t.FromTerminal.Length>0)
.Select(t => int.Parse(t.FromTerminal.Last()))
.OrderBy(t=>t)
.ToList();
UPDATE
if last not only one numer that can use regex like this
var terminalList = sourceLists.Where(t => t.TagNo == tagList)
.Where(t=>t.FromTerminal.Length>0)
.Select(t => int.Parse(Regex.Match(t.FromTerminal, #"(\d+)$").Groups[1].Value))
.OrderBy(t=>t)
.ToList();
I am quiet confused what do you wants ... as your picture says you wants the combination of TagNO and FTerminal and in the other hand your query says you wants only FTerminals in certain order ..
Now if you wants the first one then
void Abc(int tagList)
{
var sourceLists = new List<Demo>
{
new Demo { FTerminal = "200", TagNo = 1000 },
new Demo { FTerminal = "300", TagNo = 1000 },
new Demo { FTerminal = "400", TagNo = 1000 }
};
var terminalList = sourceLists
.Where(t => t.TagNo == tagList && t.FTerminal.Length > 0)
.OrderBy(i=>i.FTerminal).GroupBy(i=>i.TagNo);
}
And the second one
void Abc(int tagList)
{
var sourceLists = new List<Demo>
{
new Demo { FTerminal = "200", TagNo = 1000 },
new Demo { FTerminal = "300", TagNo = 1000 },
new Demo { FTerminal = "400", TagNo = 1000 }
};
var terminalList =
from Demo d in sourceLists
where d.TagNo == tagList
let number = int.Parse(d.FTerminal)
orderby number ascending
select number).ToList();
}
But till if you did not get the your desired answer then please knock!!!!
Hmm ... instead of jumping through hoops, just use IsInt ... problem solved ...
:)
var terminalList = sourceLists.Where(t => t.TagNo == tagList)
.Where(t=>t.FromTerminal.Length>0)
.Where(t => t.FromTerminal.IsInt() )
.Select(t => int.Parse(t.FromTerminal))
.OrderBy(t=>t)
.ToList();
(So, just added this condition .Where(t => t.FromTerminal.IsInt() ) to your selection process)
How can I perform a conditional select on a column value, where I have a preference over which value is returned. If I can't find the top choice, I settle on the next, if available, and then if not the next, etc. As it looks right now, it would take 3 total queries. Is there a way to simplify this further?
var myResult = string.Empty;
if (myTable.Where(x => x.ColumnValue == "Three").Any())
{
myResult = "Three"; // Can also be some list.First().Select(x => x.ColumnValue) if that makes it easier;
}
else if (myTable.Where(x => x.ColumnValue == "One").Any())
{
myResult = "One";
}
else if (myTable.Where(x => x.ColumnValue == "Two").Any())
{
myResult = "Two";
}
else
{
myResult = "Four";
}
You could use a string[] for your preferences:
string[] prefs = new[]{ "One", "Two", "Three" };
string myResult = prefs.FirstOrDefault(p => myTable.Any(x => x.ColumnValue == p));
if(myResult == null) myResult = "Four";
Edit Enumerable.Join is a very efficient hash table method, it also needs only one query:
string myResult = prefs.Select((pref, index) => new { pref, index })
.Join(myTable, xPref => xPref.pref, x => x.ColumnValue, (xPref, x) => new { xPref, x })
.OrderBy(x => x.xPref.index)
.Select(x => x.x.ColumnValue)
.DefaultIfEmpty("Four")
.First();
Demo
I wrote an extension method that effectively mirrors Tim Schmelter's answer (was testing this when he posted his update. :-()
public static T PreferredFirst<T>(this IEnumerable<T> data, IEnumerable<T> queryValues, T whenNone)
{
var matched = from d in data
join v in queryValues.Select((value,idx) => new {value, idx}) on d equals v.value
orderby v.idx
select new { d, v.idx };
var found = matched.FirstOrDefault();
return found != null ? found.d : whenNone;
}
// usage:
myResult = myTable.Select(x => x.ColumnValue)
.PreferredFirst(new [] {"Three", "One", "Two"}, "Four");
I've written one that will quit a little more early:
public static T PreferredFirst<T>(this IEnumerable<T> data, IList<T> orderBy, T whenNone)
{
// probably should consider a copy of orderBy if it can vary during runtime
var minIndex = int.MaxValue;
foreach(var d in data)
{
var idx = orderBy.IndexOf(d);
if (idx == 0) return d; // best case; quit now
if (idx > 0 && idx < minIndex) minIndex = idx;
}
// return the best found or "whenNone"
return minIndex == int.MaxValue ? whenNone : orderBy[minIndex];
}
I use a weighted approach in SQL where I assign a weight to each conditional value. The solution would then be found by finding the highest or lowest weight depending on your ordering scheme.
Below would be the equivalent LINQ query. Note that in this example I am assigning a lower weight a higher priority:
void Main()
{
// Assume below list is your dataset
var myList =new List<dynamic>(new []{
new {ColumnKey=1, ColumnValue ="Two"},
new {ColumnKey=2, ColumnValue ="Nine"},
new {ColumnKey=3, ColumnValue ="One"},
new {ColumnKey=4, ColumnValue ="Eight"}});
var result = myList.Select(p => new
{
ColVal = p.ColumnValue,
OrderKey = p.ColumnValue == "Three" ? 1 :
p.ColumnValue == "One" ? 2 :
p.ColumnValue == "Two" ? 3 : 4
}).Where(i=> i.OrderKey != 4)
.OrderBy(i=>i.OrderKey)
.Select(i=> i.ColVal)
.FirstOrDefault();
Console.WriteLine(result ?? "Four");
}
How about something like this:
var results = myTable.GroupBy(x => x.ColumnValue).ToList();
if (results.Contains("Three")) {
myResult = "Three";
} else if (results.Contains("One")) {
myResult = "One";
} else if (results.Contains("Two")) {
myResult = "Two";
} else {
myResult = "Four";
}
I have a program that generates 3 lists based on the contents of a text file. Now I want to look at a list and if there's an item in it more than once, I'd like to change the value to "number in list x item" and remove the duplicates from the list.
Here is the code I use to open and split up the file into the lists:
private void open_Click(object sender, EventArgs e)
{
if (inputFile.ShowDialog() == DialogResult.OK)
{
var reader = new StreamReader(File.OpenRead(inputFile.FileName));
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
if (string.IsNullOrEmpty(line)) continue;
if (line.StartsWith("#main"))
{
deck = "main";
}
if (deck == "main")
{
if (!line.StartsWith("#"))
{
int cardid = Convert.ToInt32(line.Substring(0));
MainDeck.Items.Add(Program.CardData[cardid].Name);
}
}
if (line.StartsWith("#extra"))
{
deck = "extra";
}
if (deck == "extra")
{
if (!line.StartsWith("#extra") && !line.StartsWith("!side"))
{
int cardid = Convert.ToInt32(line.Substring(0));
ExtraDeck.Items.Add(Program.CardData[cardid].Name);
}
}
if (line.StartsWith("!side"))
{
deck = "side";
}
if (deck == "side")
{
if (!line.StartsWith("!side"))
{
int cardid = Convert.ToInt32(line.Substring(0));
SideDeck.Items.Add(Program.CardData[cardid].Name);
}
}
}
reader.Close();
GenerateCode();
}
}
In other words say the item "hello" is in the list 3 times: I want to change it to be in the list only once and say "3x hello".
Use Enumerable.Distinct to remove the duplicates:
MainDeck = MainDeck.Distinct().ToList();
ExtraDeck = ExtraDeck.Distinct().ToList();
SideDeck = SideDeck.Distinct().ToList();
If you want to count the duplicates first:
int mainDeckDups = MainDeck.Count - MainDeck.Distinct().Count();
int extraDeckDups = ExtraDeck.Count - ExtraDeck.Distinct().Count();
int sideDeckDups = SideDeck.Count - SideDeck.Distinct().Count();
If you really want to show how many times an item was in the list you could use Enumerable.GroupBy, e.g.:
var mainDeckGroups = MainDeck.GroupBy(s => s)
.Select(g => new { Item = g.Key, Count = g.Count() })
.Where(x => x.Count > 1)
.OrderByDescending(x => x.Count);
foreach (var dup in mainDeckGroups)
Console.WriteLine("{0}x {1}", dup.Count, dup.Item);
// other lists ...
Something like:
var g = MainDeck.Items.GroupBy(i => i).Select(x => x.Count() +"x " + x.Key);
You can filter it out if you want to count only the word "main" there. Follow the same for other lists..
The query is not evaluated at that point. Do a .ToList() or .ToArray() to let that happen on g.
If you want to preserve order of items in the list (assuming list contains the possible duplicates)
var data = list.Select(r => result.Count(i => i == r) + "x " + r).ToList();
and just slightly modify it to have "Hello" instead of "1x Hello".
You can try it as well.
var list = new List<string> { "Hello", "World", "Hello", "Great", "World" };
var query = list.GroupBy(s => s)
.Select(g => new { Value = g.Key, Count = g.Count() });
Then,
var resultList = query.Select(result => string.Format("{0}x {1}", result.Count, result.Value)).ToList();
or above code can be replaced as below for more clarity
foreach (var result in query)
resultList.Add(string.Format("{0}x {1}", result.Count, result.Value));
Hope it helps.