How to check duplicate values in a dictionary - c#

Im having 5 users and 5 services.
I must catch if more than one user(s) have same service. So I created a dictionary:
//JMBGS represents users ids
List<string> multipleJMBGs = new List<string>();
multipleJMBGs.Add(data.jMBG);
multipleJMBGs.Add(data.secondPersonJMBG);
multipleJMBGs.Add(data.thirdPersonJMBG);
multipleJMBGs.Add(data.fourthPersonJMBG);
multipleJMBGs.Add(data.fifthPersonJMBG);
multipleJMBGs.RemoveAll(x => string.IsNullOrEmpty(x));
//object Service has Id property, and I need to check if more than one user has the same service
var serviceForClient = data?.Schedules[0]?.Service;
var serviceForFirstFamilyMember = data?.Schedules[1]?.Service;
var serviceForSecondFamilyMember = data?.Schedules[2]?.Service;
var serviceForThirdFamilyMember = data?.Schedules[3]?.Service;
var serviceForFourthFamilyMember = data?.Schedules[4]?.Service;
Dictionary<string, EmployeeTableDay.ServiceDTO> userService = new Dictionary<string, EmployeeTableDay.ServiceDTO>();
userService.Add(data.jMBG, serviceForClient);
userService.Add(data.secondPersonJMBG, serviceForFirstFamilyMember);
userService.Add(data.thirdPersonJMBG, serviceForSecondFamilyMember);
userService.Add(data.fourthPersonJMBG, serviceForThirdFamilyMember);
userService.Add(data.fifthPersonJMBG, serviceForFourthFamilyMember);
What I want to have as flag (true/false):
different users can have different services
different users can have same services
same users, can not have same service
same users can have multiple services
UPDATE #1:
List<string> multipleJMBGs = new List<string>();
multipleJMBGs.Add(data.jMBG);
multipleJMBGs.Add(data.secondPersonJMBG);
multipleJMBGs.Add(data.thirdPersonJMBG);
multipleJMBGs.Add(data.fourthPersonJMBG);
multipleJMBGs.Add(data.fifthPersonJMBG);
multipleJMBGs.RemoveAll(x => string.IsNullOrEmpty(x));
var countedSchedules = data?.Schedules?.Count();
var serviceIdForClient = 0;
var serviceIdForFirstFamilyMember = 0;
var serviceIdForSecondFamilyMember = 0;
var serviceIdForThirdFamilyMember = 0;
var serviceIdForFourthFamilyMember = 0;
List<int> servicesIds = new List<int>();
if(countedSchedules >= 1 && data?.Schedules[0]?.Service != null)
{
serviceIdForClient = data.Schedules[0].Service.Id;
servicesIds.Add(serviceIdForClient);
}
if (countedSchedules >= 2 && data?.Schedules[1]?.Service != null)
{
serviceIdForFirstFamilyMember = data.Schedules[1].Service.Id;
servicesIds.Add(serviceIdForFirstFamilyMember);
}
if (countedSchedules >= 3 && data?.Schedules[2]?.Service != null)
{
serviceIdForSecondFamilyMember = data.Schedules[2].Service.Id;
servicesIds.Add(serviceIdForSecondFamilyMember);
}
if (countedSchedules >= 4 && data?.Schedules[3]?.Service != null)
{
serviceIdForThirdFamilyMember = data.Schedules[3].Service.Id;
servicesIds.Add(serviceIdForThirdFamilyMember);
}
if (countedSchedules == 5 && data?.Schedules[4]?.Service != null)
{
serviceIdForFourthFamilyMember = data.Schedules[4].Service.Id;
servicesIds.Add(serviceIdForFourthFamilyMember);
}
servicesIds = servicesIds.Where(x => x > 0).ToList();
//because dictionary throws an exception
for (int i = 0; i < countedSchedules; i++)
{
multipleJMBGs[i] = i + " _ " + multipleJMBGs[i];
}
//create dictionary from two lists
var dic = multipleJMBGs.Zip(servicesIds, (k, v) => new { k, v }).ToDictionary(x => x.k, x => x.v);
var duplicateServices = dic.GroupBy(x => x.Value).Where(x => x.Count() > 1)
.Select(x => new { ServiceId = x.Key, Users = x.ToList() });
var duplicateUsers = dic.GroupBy(x => x.Key.Substring(3,x.Key.Length-3)).Where(x => x.Count() > 1)
.Select(x => new { User = x.Key, Services = x.ToList() });
if(duplicateServices > 1 && duplicateUsers.Count > 1)
{
//show a message that it ca't be proceeed further
}
else
{
//continue
}

You can group the services by Id to get information on duplicates:
var groups = data?.Schedules
.Select((service, index) => (service, index))
.GroupBy(x => x.service.Id);
foreach (var g in groups) {
if (g.Count() == 1) {
Console.WriteLine(
$"Service Id {g.Key} exists once for user index {g.First().index}");
} else {
string indexes = String.Join(", ", g.Select(t => t.index.ToString()));
Console.WriteLine(
$"Service Id {g.Key} is shared by user indexes {indexes}");
}
}
If you don't call multipleJMBGs.RemoveAll(...), then the indexes in the users list will match the indexes of the services. So you can easily get the JMBG assigned to a service.
var groups = data?.Schedules
.Select((service, index) => (service, index))
.GroupBy(x => x.service.Id);
foreach (var g in groups) {
if (g.Count() == 1) { // Service is unique
var service = g.First().service;
int index = g.First().index;
string user = multipleJMBGs[index];
...
} else { // Services assigned to more than one user.
var serviceId = g.Key;
var service = g.First().service;
foreach (var t in g) {
int index = t.index;
string user = multipleJMBGs[index];
...
}
...
}
}

Purely answering the question on how to check for duplicates within your dictionary:
You could group the values of your dictionary and look for duplicates that way.
var containsDuplicates = userService.GroupBy(x => x.Value).Any(x => x.Count() > 1);
If you want to actually see which services are used multiple times by which users, create the following grouping:
var duplicateServices = userService.GroupBy(x => x.Value).Where(x => x.Count() > 1)
.Select(x => new { Service = x.Key, Users = x.ToList() });
UPDATE #1:
You don't necessarily have to group up both of these lists if you just need to check if any of the two contain duplicates.
We could simply compare the total count of the lists with the count of the list when we remove all possible duplicates:
var duplicateServices = countedSchedules != data?.Schedules?.Distinct().Count();
var duplicateUsers = multipleJMBGs.Count() != multipleJMBGs.Distinct().Count();
UPDATE #2:
Sounds like you want to join the two lists together and not necessarily convert it to a dictionary, what we want to do instead is create a new List so we can group that on the combination of the two properties.
var containsDuplicates = multipleJMBGs.Zip(serviceIds, (JMBG, serviceId) => new { JMBG, serviceId })
.Where(x => !string.IsNullOrEmpty(x.JMBG) && x.serviceId > 0) // Filter AFTER zipping the two lists together
.GroupBy(x => x).Any(x => x.Count() > 1);
Do be aware that you are removing elements in both lists, meaning that the order might not be correct. You could instead add a Where clause after you zip the lists like in the example above.

Related

Rename List item when there is the same string multiple time

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;
}
}

The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'

I have the following query.
var query = Repository.Query<Product>()
.Where(p => !p.IsDeleted && p.Article.ArticleSections.Count() > 0)
.Select(p => new
{
OfficeId = p.TariffCategory.Office.Id,
Office = p.TariffCategory.Office.Name,
Category = p.TariffCategory.Description,
ArticleId = p.Article.Id,
Article = p.Article.Title,
Destinations = p.ProductDestinations.OrderBy(pd => pd.Destination.Description).Select(pd => new { Id = pd.DestinationId, Name = pd.Destination.Description }),
GlobalDestinations = p.AllDestinationsInOffice,
p.Article.LastReviewedDate,
p.Article.CreatedDate,
p.Article.CreatedByEmployee
});
query = query.Concat(Repository.Query<Package>()
.Where(pkg => !pkg.IsDeleted && pkg.Article.ArticleSections.Count() > 0)
.Select(pkg => new
{
OfficeId = pkg.TariffCategory.Office.Id,
Office = pkg.TariffCategory.Office.Name,
Category = pkg.TariffCategory.Description,
ArticleId = pkg.Article.Id,
Article = pkg.Article.Title,
Destinations = pkg.PackageDestinations.OrderBy(pd => pd.Destination.Description).Select(pd => new { Id = pd.DestinationId, Name = pd.Destination.Description }),
GlobalDestinations = pkg.AllDestinationsInOffice,
pkg.Article.LastReviewedDate,
pkg.Article.CreatedDate,
pkg.Article.CreatedByEmployee
}));
query = query.Concat(Repository.Query<Backgrounder>()
.Where(bkgd => !bkgd.IsDeleted && bkgd.Article.ArticleSections.Count() > 0)
.Select(bkgd => new
{
OfficeId = bkgd.TariffCategory.Office.Id,
Office = bkgd.TariffCategory.Office.Name,
Category = bkgd.TariffCategory.Description,
ArticleId = bkgd.Article.Id,
Article = bkgd.Article.Title,
Destinations = bkgd.BackgrounderDestinations.OrderBy(bd => bd.Destination.Description).Select(bd => new { Id = bd.DestinationId, Name = bd.Destination.Description }),
GlobalDestinations = bkgd.AllDestinationsInOffice,
bkgd.Article.LastReviewedDate,
bkgd.Article.CreatedDate,
bkgd.Article.CreatedByEmployee
}));
// Apply filters
if (OfficeIds.Any())
query = query.Where(a => OfficeIds.Contains(a.OfficeId));
if (DestinationIds.Any())
query = query.Where(a => a.GlobalDestinations || a.Destinations.Any(d => DestinationIds.Contains(d.Id)));
if (!string.IsNullOrEmpty(ArticleTitle))
query = query.Where(a => a.Article.Contains(ArticleTitle));
if (!string.IsNullOrEmpty(TariffCategory))
query = query.Where(a => a.Category.Contains(TariffCategory));
// Sort results
query = query.OrderBy(a=> a.Office).ThenBy(a => a.Category).ThenBy(a => a.Article);
var articles = query.ToList();
However, when I run this query I get an exception.
The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'
The query searches through articles in my database. Because articles can be related to a Product, Package or Backgrounder, and I need information from the related table, I concatenate separate queries for each of those items.
I've narrowed it down to the assignment to Destinatons. Apparently, this constitute a query within the query associated with Concat(). (If I remove the second two queries and the associated Concat() calls, it works fine.)
After looking at this a while, I'm having trouble seeing another way to construct my query without making it much, much slower.
Does anyone see any tricks I might have missed to work around the exception?
Unfortunately there are no tricks. The only reasonable way I see to make this work without totally rewriting it is to execute the queries separately (applying all possible filters) and then do Concat and ordering in memory like this:
var queries = new []
{
Repository.Query<Product>()
.Where(p => !p.IsDeleted && p.Article.ArticleSections.Count() > 0)
.Select(p => new
{
OfficeId = p.TariffCategory.Office.Id,
Office = p.TariffCategory.Office.Name,
Category = p.TariffCategory.Description,
ArticleId = p.Article.Id,
Article = p.Article.Title,
Destinations = p.ProductDestinations.OrderBy(pd => pd.Destination.Description).Select(pd => new { Id = pd.DestinationId, Name = pd.Destination.Description }),
GlobalDestinations = p.AllDestinationsInOffice,
p.Article.LastReviewedDate,
p.Article.CreatedDate,
p.Article.CreatedByEmployee
}),
Repository.Query<Package>()
.Where(pkg => !pkg.IsDeleted && pkg.Article.ArticleSections.Count() > 0)
.Select(pkg => new
{
OfficeId = pkg.TariffCategory.Office.Id,
Office = pkg.TariffCategory.Office.Name,
Category = pkg.TariffCategory.Description,
ArticleId = pkg.Article.Id,
Article = pkg.Article.Title,
Destinations = pkg.PackageDestinations.OrderBy(pd => pd.Destination.Description).Select(pd => new { Id = pd.DestinationId, Name = pd.Destination.Description }),
GlobalDestinations = pkg.AllDestinationsInOffice,
pkg.Article.LastReviewedDate,
pkg.Article.CreatedDate,
pkg.Article.CreatedByEmployee
}),
Repository.Query<Backgrounder>()
.Where(bkgd => !bkgd.IsDeleted && bkgd.Article.ArticleSections.Count() > 0)
.Select(bkgd => new
{
OfficeId = bkgd.TariffCategory.Office.Id,
Office = bkgd.TariffCategory.Office.Name,
Category = bkgd.TariffCategory.Description,
ArticleId = bkgd.Article.Id,
Article = bkgd.Article.Title,
Destinations = bkgd.BackgrounderDestinations.OrderBy(bd => bd.Destination.Description).Select(bd => new { Id = bd.DestinationId, Name = bd.Destination.Description }),
GlobalDestinations = bkgd.AllDestinationsInOffice,
bkgd.Article.LastReviewedDate,
bkgd.Article.CreatedDate,
bkgd.Article.CreatedByEmployee
}),
};
// Apply filters
if (OfficeIds.Any())
for (int i = 0; i < queries.Length; i++) queries[i] = queries[i].Where(a => OfficeIds.Contains(a.OfficeId));
if (DestinationIds.Any())
for (int i = 0; i < queries.Length; i++) queries[i] = queries[i].Where(a => a.GlobalDestinations || a.Destinations.Any(d => DestinationIds.Contains(d.Id)));
if (!string.IsNullOrEmpty(ArticleTitle))
for (int i = 0; i < queries.Length; i++) queries[i] = queries[i].Where(a => a.Article.Contains(ArticleTitle));
if (!string.IsNullOrEmpty(TariffCategory))
for (int i = 0; i < queries.Length; i++) queries[i] = queries[i].Where(a => a.Category.Contains(TariffCategory));
// Switch to LINQ to Objects and concatenate the results
var result = queries.Select(query => query.AsEnumerable()).Aggregate(Enumerable.Concat);
// Sort results
result = result.OrderBy(a=> a.Office).ThenBy(a => a.Category).ThenBy(a => a.Article);
var articles = result.ToList();

DropDown SelectedIndex set to 0 if Selection Repeated

I'm having this linq query which checks for all 6 dropdowns whether there is any repetition in selction.
To do that , currently I'm setting SelectedIndex to 0 for all dropdowns. Instead of that , i want those two dropdowns.SelectedIndex=0 which has same SelectedValue.
var allIndexes = new List<int>
{
drpdwnlst_Seq1.SelectedIndex,
drpdwnlst_Seq2.SelectedIndex,
drpdwnlst_Seq3.SelectedIndex,
drpdwnlst_Seq4.SelectedIndex,
drpdwnlst_Seq5.SelectedIndex,
drpdwnlst_Seq6.SelectedIndex
};
var noSelectedIndexIsTheSame = allIndexes.Where(x => x != 0)
.GroupBy(x => x)
.All(x => x.Count() == 1);
if (!noSelectedIndexIsTheSame)
{
ScriptManager.RegisterStartupScript(Page, Page.GetType(), "AdminUserError", "alert('Selection Rrepeated.'); ", true);
drpdwnlst_Seq1.SelectedIndex = 0;
drpdwnlst_Seq2.SelectedIndex = 0;
drpdwnlst_Seq3.SelectedIndex = 0;
drpdwnlst_Seq4.SelectedIndex = 0;
drpdwnlst_Seq5.SelectedIndex = 0;
drpdwnlst_Seq6.SelectedIndex = 0;
}
You could change the List to the following definition, (so you can keep most of your logic)
List<KeyValuePair<DropDownList, int>> allIndexes = new List<KeyValuePair<DropDownList, int>>
{
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq1, drpdwnlst_Seq1.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq2, drpdwnlst_Seq2.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq3, drpdwnlst_Seq3.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq4, drpdwnlst_Seq4.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq5, drpdwnlst_Seq5.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq6, drpdwnlst_Seq6.SelectedIndex)
};
var groups = allIndexes.Where(x => x.Value != 0).GroupBy(x => x.Value);
boolean noSelectedIndexIsTheSame = groups.All(g => g.Count() == 1);
if (!noSelectedIndexIsTheSame)
{
ScriptManager.RegisterStartupScript(Page, Page.GetType(), "AdminUserError", "alert('Selection Rrepeated.'); ", true);
foreach (var g in groups)
{
if (g.Count() == 1)
{
continue;
}
foreach (var kvp in g)
{
kvp.Key.SelectedIndex = 0;
}
}
}
this way, you have the reference of your combobox + the current selected index, you group by the KeyValuePair.Value, and you have a separate bool checking if no indexes are the same. If they are, it will loop the groups, and in case more than 1 was found per group, reset those

how can i count duplicates in a list and change the item value

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.

Grouping each 500 elements of array according to array elements

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);
}

Categories

Resources