I am looking for simple LINQ to solve this:
string[] breakups = new[]
{
"YQ:50/BF:50/YR:50",
"YQ:50/SR:50",
"YQ:50/BF:50/YR:50",
"XX:00 .... and so on"
};
// LINQ expression
string expectedResult = "YQ:150/BF:100/YR:100/SR:50";
My alternate solution as follows
public static string CalcRTBreakup(string pstrBreakup)
{
string lstrBreakup = pstrBreakup;
try
{
string[] lstrArrRB = lstrBreakup.Split('#');
string[] lstrArrBreakupSplit = new string[lstrBreakup.Split('#')[0].Split('|').Length];
for (int count = 0; count < lstrArrRB.Length; count++)
{
string[] lstrArrtemp = lstrArrRB[count].Split('|');
for (int countinner = 0; countinner < lstrArrtemp.Length; countinner++)
{
if (string.IsNullOrEmpty(lstrArrBreakupSplit[countinner]))
{
if (string.IsNullOrEmpty(lstrArrtemp[countinner]))
continue;
lstrArrBreakupSplit[countinner] = lstrArrtemp[countinner];
}
else
{
if (string.IsNullOrEmpty(lstrArrtemp[countinner]))
continue;
lstrArrBreakupSplit[countinner] += "/" + lstrArrtemp[countinner];
}
}
}
for (int count = 0; count < lstrArrBreakupSplit.Length; count++)
{
if (string.IsNullOrEmpty(lstrArrBreakupSplit[count]))
continue;
lstrArrBreakupSplit[count] = CalcRTBreakupDict(lstrArrBreakupSplit[count].TrimEnd('/')).TrimEnd('/');
}
lstrBreakup = string.Empty;
foreach (string strtemp in lstrArrBreakupSplit)
{
lstrBreakup += strtemp + '|';
}
return lstrBreakup;
}
catch (Exception)
{
return "";
}
}
public static string CalcRTBreakupDict(string pstrBreakup)
{
string lstrBreakup = pstrBreakup;
Dictionary<string, double> ldictDreakup = new Dictionary<string, double>();
try
{
lstrBreakup = lstrBreakup.TrimEnd('/').Trim();
string[] lstrArrBreakup = lstrBreakup.Split('/');
foreach (string strBr in lstrArrBreakup)
{
string[] lstrBreakupCode = strBr.Split(':');
if (!ldictDreakup.Keys.Contains(lstrBreakupCode[0]))
{
double lintTemp = 0; double.TryParse(lstrBreakupCode[1], out lintTemp);
ldictDreakup.Add(lstrBreakupCode[0], lintTemp);
}
else
{
double lintTemp = 0; double.TryParse(lstrBreakupCode[1], out lintTemp);
lintTemp = lintTemp + ldictDreakup[lstrBreakupCode[0]];
ldictDreakup.Remove(lstrBreakupCode[0]);
ldictDreakup.Add(lstrBreakupCode[0], lintTemp);
}
}
lstrBreakup = string.Empty;
foreach (string dictKey in ldictDreakup.Keys)
{
lstrBreakup += dictKey + ":" + ldictDreakup[dictKey] + "/";
}
return lstrBreakup;
}
catch (Exception)
{
return pstrBreakup;
}
}
string[] breakups =
{
"YQ:50/BF:50/YR:50",
"YQ:50/SR:50",
"YQ:50/BF:50/YR:50",
"XX:00"
};
var groups = from line in breakups // these are our items in the array
from item in line.Split('/') // each one will be split up at '/'
let pair = item.Split(':') // each pair is split at ':'
let key = pair[0] // our key is the first item...
let value = int.Parse(pair[1]) // and the value is the second
group value by key // let's group by key
into singleGroup
let sum = singleGroup.Sum() // and build each group's sum
where sum > 0 // filter out everything <= 0
select singleGroup.Key + ":" + sum; // and build the string
var result = string.Join("/", groups);
If you don't need an ordering by value, you can simply do
var res = string.Join("/", breakups
.SelectMany(m => m.Split('/'))
.Select(x => x.Split(':'))
.GroupBy(m => m[0])
.Select(m => string.Format("{0}:{1}", m.Key, m.Sum(g => Int32.Parse(g[1])))));
if you need ordering
var res = string.Join("/", breakups
.SelectMany(m => m.Split('/'))
.Select(x => x.Split(':'))
.GroupBy(m => m[0])
.Select(m => new
{
key = m.Key,
val = m.Sum(g => Int32.Parse(g[1]))
})
.OrderByDescending(m => m.val)
.Select(m => string.Format("{0}:{1}", m.key, m.val)));
var sums = breakups.SelectMany(breakup => breakup.Split('/'))
.Select(s => new { Code = s.Substring(0, 2), Value = int.Parse(s.Substring(2)) })
.GroupBy(pair => pair.Code)
.Select(group => string.Format("{0}/{1}", group.Key, group.Sum(x => x.Value)));
string result = string.Join("/", sums);
The code may contain syntax errors, cause I've not tested it.
Looking at the result I am assuming you add the values after the semicolon where the label before the semicolon match.
It can be treated as a little fun quiz, maybe more suited to another stackexchange site, but anyway.
This can be achieved with a simple (but not very short) linq expression:
breakups
.Select(c => c.Split('/'))
.SelectMany(c => c)
.Select(c => new
{
Label = c.Split(':')[0],
Value = Convert.ToInt32(c.Split(':')[1])
})
.GroupBy(c => c.Label)
.Select(c => new
{
Label = c.Key,
Value = c.Sum(x => x.Value)
})
.OrderByDescending(c => c.Value)
.Select(c => c.Label + ":" + c.Value)
.Aggregate((s1,s2) => s1 + "/" + s2)
Related
I have List<string> {"", "1,5,4", "h", "5,8", "1"}. I need to divide into 3 List<int>. This is my code:
var parseString = condition.Trim().Split(separator).ToList();
var numberSections = new List<string>();
var numberRow = new List<string>();
var numberCell = new List<string>();
foreach (var str in parseString) {
if (int.TryParse(str.Substring(0, 1), out i) && numberSections.Count == 0) {
numberSections.Add(str);
parseString.Remove(str);
}
if (int.TryParse(str.Substring(0, 1), out i) && numberRow.Count == 0) {
numberRow.Add(str);
parseString.Remove(str);
}
if (int.TryParse(str.Substring(0, 1), out i) && numberCell.Count == 0) {
numberCell.Add(str);
parseString.Remove(str);
}
}
But it do not working. How I can do it?
Here is a LINQ version for it
var result = list.Select(x => x.Split(",".ToCharArray(),
StringSplitOptions.RemoveEmptyEntries)) // now we have List<List<string>>
.Select(x => x.Select(y =>
{
int value;
var isInt = int.TryParse(y, out value);
return isInt ? value : (int?)null;
})) // convert each element of inner list to null or its int values
// we have a List<List<int?>>
.Where(x => x.Any() && x.All(y => y.HasValue)) // only select lists which contains only integers
.ToList();
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 string array[2] as follows:
1st Array 2nd Aray
"100101" "Testing123"
"100102" "Apple123"
"100101" "Dog123"
"100104" "Cat123"
"100101" "Animal123"
I would like to concatenate all elements of the 2nd array if the elements in the first array match.
For example elements of the first array that match are "100101", "100101" and "100101". So a string with the concatenated values of the respective 2nd array would be as follows:
"Testing123 Dog123 Animal123"
How could this be achieved elegantly?
I did it this way:
var results =
array1
.Zip(array2, (x1, x2) => new { x1, x2 })
.ToLookup(x => x.x1, x => x.x2)
.Select(x => new { x.Key, Value = String.Join(" ", x), });
I got this result:
If you needed to extract the results in a different way it wouldn't be too hard to fiddle with my method to get what you need.
You can use GroupBy:
var strings = array1.Select((s,index) => new{ s, index })
.GroupBy(x => x.s)
.Select(g =>
string.Join(" ", g.Select(x => array2.ElementAtOrDefault(x.index))));
foreach(string s in strings)
Console.WriteLine(s);
If you want to concatenate only strings which are duplicates in the first array, add this Where:
// ...
.GroupBy(x => x.s)
.Where(g => g.Count() > 1)
// ...
Here's a Demo
var indices = array1.Select((i, s) => new {Index = i, Str = s})
.Where(e => e.Str == "100101")
.Select(e => e.Index);
string result = string.Join(" ", array2.Select((i, s) => new {Index = i, Str = s})
.Where(e => indices.Contains(e.Index))
.Select(e => e.Str));
assuming both arrays are the same length, this should give you the output you need.
var array1 = new[] {"100101", "100102", "100101", "100104","100101" };
var array2 = new[] { "Testing123", "Apple123", "Dog123","Cat123", "Animal123" };
var result = new Dictionary<string, string>();
for (int i = 0; i < array1.Length; i++)
{
// if the value has been found before
if( result.ContainsKey( array1[i] ) ) {
result[array1[i]] += " " + array2[i]; // append to existing "matched" entry
}
else {
result.Add(array1[i], array2[i]); // add new unique value
}
}
You can zip these two arrays as they are of same size. Then group the elements by first array value.
Then join the elements.
I wrote a sample program using linq
string[] array1 = new string[]{"100101","100102","100101","100104","100101"};
string[] array2 = new string[] { "Testing123", "Apple123", "Dog123", "Cat123", "Animal123" };
var concatenatedString = array1.Zip(array2, (x, y) => new { First = x, Second = y }).GroupBy(t => t.First).Select(t=> string.Join(" ",t.Select(s=> s.Second))).ToList();
The result will contain a list of concatenated strings.
Hope it Helps
var arr1 = new [] { "100101", "100102", "100101", "100104", "100101" };
var arr2 = new [] { "Testing123", "Apple123", "Dog123", "Cat123", "Animal123" };
var result = string.Join(" ", arr2.Where((a, i) => i < arr1.Length && arr1[i] == "100101"));
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.
I have an array of 2000 strings. The strings are: "art", "economy", "sport" and "politic". I want to group each 500 elements and get their counts
Could anyone help please?
Another solution:
var count = 0;
var dictionaries =
strings.GroupBy(s => count++ / 500)
.Select(g => g.Distinct().ToDictionary(k => k, k => g.Count(s => s == k)))
.ToList();
This will create a List<Dictionary<string, int>>. Each dictionary represents a tally of 500 elements (or possibly less for the last dictionary), where the keys are strings and the values are the number of occurrences of the string among the 500 elements the dictionary represents.
There is no requirement to hardcode all the possible values that may be encountered.
For the maximum possible performance you can also use this version:
var count = 0;
var dictionaries =
strings.GroupBy(s => count++ / 500)
.Select(g => g.Aggregate(
new Dictionary<string, int>(),
(d, w) => { d[w] = (d.ContainsKey(w) ? d[w] + 1 : 1); return d; })
)
.ToList();
This version iterates over each element in your source array exactly once. The output is in the same format as the first version.
var result = strings.Select((s, i) => new { s, i })
.GroupBy(x => x.i / 500)
.Select(x => x.GroupBy(y => y.s)
.Select(z => new {
Name=z.Key,
Count=z.Count()
}).ToList())
.ToList();
Try
var grouping = Enumerable.Range(0,2000)
.Select(i => i / 500)
.Zip(Strings, (i,s) => new { Group = i, Str = s})
.GroupBy(anon => anon.Group,
anon => anon.Str,
(key,g) => new
{
Key = key,
Art = g.Count(str => str == "art"),
Economy = g.Count(str => str == "economy"),
Politic = g.Count(str => str == "politic"),
Sport= g.Count(str => str == "sport")
});
foreach(anon in grouping)
{
//textbox logic OP will have to change to suit
TextBox1.WriteLine(String.Format("Group: {0}", anon.Key));
TextBox1.WriteLine(String.Format("Art: {0}",anon.Art));
TextBox1.WriteLine(String.Format("Economy: {0}",anon.Economy ));
TextBox1.WriteLine(String.Format("Politic: {0}",anon.Politic ));
TextBox1.WriteLine(String.Format("Sport: {0}",anon.Sport));
}
Alternatively (as per Snowbear)
var grouping = Strings.Select((s,i) => new { Group = i / 500, Str = s})
.GroupBy(anon => anon.Group,
anon => anon.Str,
(key,g) => new
{
Key = key,
Art = g.Count(str => str == "art"),
Economy = g.Count(str => str == "economy"),
Politic = g.Count(str => str == "politic"),
Sport= g.Count(str => str == "sport")
});
foreach(anon in grouping)
{
//textbox logic OP will have to change to suit
TextBox1.WriteLine(String.Format("Group: {0}",anon.Key + 1));
TextBox1.WriteLine(String.Format("Art: {0}",anon.Art));
TextBox1.WriteLine(String.Format("Economy: {0}",anon.Economy ));
TextBox1.WriteLine(String.Format("Politic: {0}",anon.Politic ));
TextBox1.WriteLine(String.Format("Sport: {0}",anon.Sport));
}
int CountElementsInGroup = 500;
//from 500 to 1000
int NumberGroup = 2;
string[] GroupTypes = new string[4] { "art", "economy", "sport", "politic" };
//Fill example array
string[] arr = new string[2000];
Random rand = new Random();
for (int i = 0; i < arr.Length;i++ )
arr[i] = GroupTypes[rand.Next(0, 3)];
var res = (from p in arr.Skip((NumberGroup - 1) * CountElementsInGroup).Take(CountElementsInGroup)
group p by p into g
select new GroupCountClass { GroupName = g.Key, GroupCount = g.Count() });
textBox1.Text = "";
foreach (GroupCountClass c in res)
{
textBox1.Text += String.Format("GroupName:{0} Count:{1};",c.GroupName,c.GroupCount);
}