I have a List like below
List<Product> products = new List<Product>();
Add(new Product { ProductId = "abc", Type = "Normal", Status = "1" });
Add(new Product { ProductId = "def", Type = "Normal", Status = "2" });
Add(new Product { ProductId = "ghi", Type = "VIP" , Status = "1" });
Add(new Product { ProductId = "jkl", Type = "Group", Status = "1" });
public Product Add(Product item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
products.Add(item);
return item;
}
I want to count like:
Type: Normal Count: 2
Type: VIP Count: 1
Type: Group Count:1
from earlier help at below location
Unique object counts are not getting counted in form of String but char
I wrote code
var groups = products.GroupBy(x => new { x.Type, x.Status }).Select(g => string.Format("Type: {0} Count: {1}", g.Key, g.Count()));
When I run above I get text in groups as below
System.Linq.Enumerable+WhereSelectEnumerableIterator2[System.Linq.IGrouping2[<>f__AnonymousType0`2[System.String,System.Int32],LocalServer.Product],System.String]##
Not sure how to fix, or what I am doing wrong.
This should solve your issues.
List<Product> products = new List<Product>();
products.Add(new Product { ProductId = "abc", Type = "Normal", Status = "1" });
products.Add(new Product { ProductId = "def", Type = "Normal", Status = "2" });
products.Add(new Product { ProductId = "ghi", Type = "VIP" , Status = "3" });
products.Add(new Product { ProductId = "jkl", Type = "Group", Status = "1" });
IEnumerable<string> groupedProducts = products.GroupBy(product => product.Type)
.Select(grouping => string.Format("Type: {0} Count: {1}", grouping.Key, grouping.Count()));
foreach (var groupedProduct in groupedProducts)
{
Console.WriteLine(groupedProduct);
}
The problem was that you were trying to group by a number of properties, since you only want to count by Type, use .GroupBy(product => product.Type), then use the .Select() to project the results in any fashion you want.
You need to use the group Key property's Type property to get the type name becouse you are grouping by a new annonymous type.
var groups = products.GroupBy(x => new { x.Type, x.Status }).Select(g => string.Format("Type: {0} Count: {1}", g.Key.Type, g.Count()));
Related
I was looking to get items from ListA, where the value of Id is same in both of the lists, and the count of Id must be more than 1 in list A or list B
var items = itemsA.Where(x => itemsB.Select(y => y.Id == x.Id).Count() > 1);
This gives me the result where same Ids in itemsB is more then 1, I want to use a or condition to check for the same counter in itemsA
Eg 1:
ListA=[{"id"=1,"name="abc"},{"id=1, "name"="def"}]
ListB=[{"id=2","name="xyz"}, {"id=1, "name"="mno"}]
Should return [{"id"=1,"name="abc"},{"id=1, "name"="def"}] because id =1 exists in listB and the count of id with value 1 in listA is more then 1.
Eg 2:
ListA=[{"id"=2,"name="abc"},{"id=1, "name"="def"}]
ListB=[{"id=1","name="xyz"}, {"id=1, "name"="mno"}]
should return {"id=1, "name"="def"} because common id in both list is 1 and the count of id with value 1 in ListB is more then 1.
I am not certain this is the best solution, but as far as I've understood the question, it should be a solution.
Assuming you have an Item class as follows:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
and define itemsA and itemsB as List<Item>s, you can first find all Ids that are present in both lists, then select the applicable items from itemsA based on occurrence of each Id in either list:
IEnumerable<int> idsInBothItemLists = itemsA
.Select(a => a.Id)
.Intersect(itemsB.Select(b => b.Id))
.Distinct();
List<Item> items = itemsA
.Where(a => idsInBothItemLists.Contains(a.Id))
.GroupBy(a => a.Id)
.Where(gr =>
gr.Skip(1).Any() ||
itemsB.Where(b => b.Id == gr.Key).Skip(1).Any())
.SelectMany(gr => gr.Select(item => item))
.ToList();
(.Skip(1).Any() serves the same purpose as .Count() > 1 in your original code; it simply checks whether there are any items left after skipping the first item.)
Printing the output from the suggested population of itemsA and itemsB
foreach (var entry in items)
{
Console.WriteLine(entry.Id + " " + entry.Name);
}
e.g. for input
var itemsA = new List<Item>
{
new Item { Id = 1, Name = "abc" },
new Item { Id = 3, Name = "def" },
new Item { Id = 1, Name = "ghi" },
new Item { Id = 2, Name = "jkl" }
};
var itemsB = new List<Item>
{
new Item { Id = 2, Name = "xyz" },
new Item { Id = 2, Name = "jkl" },
new Item { Id = 1, Name = "mno" },
new Item { Id = 3, Name = "pqr" }
};
gives
1 abc
1 ghi
2 jkl
I have the below set of data
Where each City belongs to a specific Department, which belongs to a specific Region, which belongs to a specific Country (in this case there is only one country: France).
This data is contained in a CSV file which I can read from on a row-by-row basis, however my goal is to convert this data into a tree structure (with France being at the root).
Each of these nodes will be given a specific Id value, which is something I've already gone and done, but the tricky part is that each node here must also contain a ParentId (for instance Belley and Gex need the ParentId of Ain, but Moulins and Vichy need the ParentId of Aller).
Below is a snippet of code I've written that has assigned an Id value to each name in this data set, along with some other values:
int id = 0;
List<CoverageAreaLevel> coverageAreas = GetCoverageAreaDataFromCsv(path, true);
List<LevelList> levelLists = new List<LevelList>
{
new LevelList { Names = coverageAreas.Select(a => a.Level1).Distinct().ToList(), Level = "1" },
new LevelList { Names = coverageAreas.Select(a => a.Level2).Distinct().ToList(), Level = "2" },
new LevelList { Names = coverageAreas.Select(a => a.Level3).Distinct().ToList(), Level = "3" },
new LevelList { Names = coverageAreas.Select(a => a.Level4).Distinct().ToList(), Level = "4" }
};
List<CoverageArea> newCoverageAreas = new List<CoverageArea>();
foreach (LevelList levelList in levelLists)
{
foreach (string name in levelList.Names)
{
CoverageArea coverageArea = new CoverageArea
{
Id = id++.ToString(),
Description = name,
FullDescription = name,
Level = levelList.Level
};
newCoverageAreas.Add(coverageArea);
}
}
The levelLists variable contains a sort-of heirarchical structure of the data that I'm looking for, but none of the items in that list are linked together by anything.
Any idea of how this could be implemented? I can manually figure out each ParentId, but I'd like to automate this process, especially if this needs to be done in the future.
The solution from #Camilo is really good and pragmatic. I would also suggest the use of a tree.
A sample implementation:
var countries = models.GroupBy(xco => xco.Country)
.Select((xco, index) =>
{
var country = new Tree<String>();
country.Value = xco.Key;
country.Children = xco.GroupBy(xr => xr.Region)
.Select((xr, xrIndex) =>
{
var region = new Tree<String>();
region.Value = xr.Key;
region.Parent = country;
region.Children =
xr.GroupBy(xd => xd.Department)
.Select((xd, index) =>
{
var department = new Tree<String>();
department.Value = xd.Key;
department.Parent = region;
department.Children = xd
.Select(xc => new Tree<String> { Value = xc.City, Parent = department });
return department;
});
return region;
});
return country;
});
public class Tree<T>
{
public IEnumerable<Tree<T>> Children;
public T Value;
public Tree<T> Parent;
}
One way you could solve this is by building dictionaries with the names and IDs of each level.
Assuming you have data like this:
var models = new List<Model>
{
new Model { Country = "France", Region = "FranceRegionA", Department = "FranceDept1", City = "FranceA" },
new Model { Country = "France", Region = "FranceRegionA", Department = "FranceDept1", City = "FranceB" },
new Model { Country = "France", Region = "FranceRegionA", Department = "FranceDept2", City = "FranceC" },
new Model { Country = "France", Region = "FranceRegionB", Department = "FranceDept3", City = "FranceD" },
new Model { Country = "Italy", Region = "ItalyRegionA", Department = "ItalyDept1", City = "ItalyA" },
new Model { Country = "Italy", Region = "ItalyRegionA", Department = "ItalyDept2", City = "ItalyB" },
};
You could do something like this, which can probably be improved further if needed:
var countries = models.GroupBy(x => x.Country)
.Select((x, index) => Tuple.Create(x.Key, new { Id = index + 1 }))
.ToDictionary(x => x.Item1, x => x.Item2);
var regions = models.GroupBy(x => x.Region)
.Select((x, index) => Tuple.Create(x.Key, new { ParentId = countries[x.First().Country].Id, Id = index + 1 }))
.ToDictionary(x => x.Item1, x => x.Item2);
var departments = models.GroupBy(x => x.Department)
.Select((x, index) => Tuple.Create(x.Key, new { ParentId = regions[x.First().Region].Id, Id = index + 1 }))
.ToDictionary(x => x.Item1, x => x.Item2);
var cities = models
.Select((x, index) => Tuple.Create(x.City, new { ParentId = departments[x.Department].Id, Id = index + 1 }))
.ToDictionary(x => x.Item1, x => x.Item2);
The main idea is to leverage the index parameter of the Select method and the speed of dictionaries to find the parent ID.
Sample output from a fiddle:
countries:
[France, { Id = 1 }],
[Italy, { Id = 2 }]
regions:
[FranceRegionA, { ParentId = 1, Id = 1 }],
[FranceRegionB, { ParentId = 1, Id = 2 }],
[ItalyRegionA, { ParentId = 2, Id = 3 }]
departments:
[FranceDept1, { ParentId = 1, Id = 1 }],
[FranceDept2, { ParentId = 1, Id = 2 }],
[FranceDept3, { ParentId = 2, Id = 3 }],
[ItalyDept1, { ParentId = 3, Id = 4 }],
[ItalyDept2, { ParentId = 3, Id = 5 }]
cities:
[FranceA, { ParentId = 1, Id = 1 }],
[FranceB, { ParentId = 1, Id = 2 }],
[FranceC, { ParentId = 2, Id = 3 }],
[FranceD, { ParentId = 3, Id = 4 }],
[ItalyA, { ParentId = 4, Id = 5 }],
[ItalyB, { ParentId = 5, Id = 6 }]
I would like to know if you can suggest me an efficient way to update a list of items in c#. Here is a generic example:
If CurrentList is
[ {Id: 154, Name: "George", Salary: 10 000}
{Id: 233, Name: "Alice", Salary: 10 000}]
And NewList is
[ {Id: 154, Name: "George", Salary: 25 000}
{Id: 234, Name: "Bob", Salary: 10 000}]
Then the result should be:
[{Id: 154, Name: "George", Salary: 25 000}
{Id: 234, Name: "Bob", Salary: 10 000} ]
I don't want just to clear the first one and use the values from the second one, but want to update the ones with the same ID, remove the ones that have been deleted and add any new ones.
Thanks in advance.
I would do something like this: (for ordinairy lists)
// the current list
var currentList = new List<Employee>();
currentList.Add(new Employee { Id = 154, Name = "George", Salary = 10000 });
currentList.Add(new Employee { Id = 233, Name = "Alice", Salary = 10000 });
// new list
var newList = new List<Employee>();
newList.Add(new Employee { Id = 154, Name = "George", Salary = 25000 });
newList.Add(new Employee { Id = 234, Name = "Bob", Salary = 10000 });
// clean up
foreach (var oldEmployee in currentList.ToArray())
if (!newList.Any(item => oldEmployee.Id == item.Id))
currentList.Remove(oldEmployee);
// check if the new item is found within the currentlist.
// If so? update it's values else add the object.
foreach (var newEmployee in newList)
{
var oldEmployee = currentList.FirstOrDefault(item => item.Id == newEmployee.Id);
if (oldEmployee == null)
{
// add
currentList.Add(newEmployee);
}
else
{
// modify
oldEmployee.Name = newEmployee.Name;
oldEmployee.Salary = newEmployee.Salary;
}
}
You can speed it up, using dictionaries, but that's not your question (for now)
You can do it with use of for loop and Linq expression:
for (int i = 0; i < NewList.Count; i++)
{
var record = CurrentList.FirstOrDefault(item => item.Id == NewList[i].Id);
if (record == null) { CurrentList.Add(NewList[i]); }
else { record.Id = NewList[i].Id; record.Name = NewList[i].Name; record.Salary = NewList[i].Salary; }
}
CurrentList.RemoveAll(item => NewList.FirstOrDefault(item2 => item2.Id == item.Id) == null);
Example of usage:
Example
A LINQ'y version wrapped in an extension method, could modified to be generic if 'Id' is on a interface of some sort.
The merge Action could potentially be a Merge() method on entity objects such as employee but I chose to use a delegate here .
public class Tests
{
[Test]
public void MergeSpike()
{
// the current list
var currentList = new List<Employee>();
currentList.Add(new Employee { Id = 154, Name = "George", Salary = 10000 });
currentList.Add(new Employee { Id = 233, Name = "Alice", Salary = 10000 });
// new list
var newList = new List<Employee>();
newList.Add(new Employee { Id = 154, Name = "George", Salary = 25000 });
newList.Add(new Employee { Id = 234, Name = "Bob", Salary = 30000 });
currentList.Merge(newList, (o, n) =>
{
if(o.Id != n.Id) throw new ArgumentOutOfRangeException("Attempt to merge on mismatched IDs");
o.Name = n.Name;
o.Salary = n.Salary;
});
Assert.That(currentList.Count(), Is.EqualTo(2));
Assert.That(currentList.First(c => c.Id == 154).Salary, Is.EqualTo(25000));
Assert.That(currentList.Any(c => c.Id == 233), Is.False);
Assert.That(currentList.First(c => c.Id == 234).Salary, Is.EqualTo(30000));
}
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
}
public static class EmployeeListExtensions
{
public static void Merge(this List<Employee> currentList, IEnumerable<Employee> newList, Action<Employee, Employee> merge)
{
// Updates
currentList.Where(e => newList.Any(n => n.Id == e.Id))
.ToList().ForEach(e => merge(e, newList.First(n1 => n1.Id == e.Id)));
// Deletes
var remove = currentList.Where(cl => newList.All(nl => cl.Id != nl.Id)).ToList();
currentList.RemoveAll(e => remove.Any(r => r.Id == e.Id));
// Inserts
currentList.AddRange(newList.Where(nl => currentList.Any(c => c.Id != nl.Id)));
}
}
I have the following list of TestParam... This is just a parameter list that is doing to determine how a query is going to be run. In the following case, the expected result would be to be executed against all the combinations of different parameters. Hence, a list of lists, with CustomerId 33 together with each product Id available in the list...
List<TestParam> testList = new List<TestParam>();
testList.Add(new TestParam() { Name = "CustomerId", Value = "33" });
testList.Add(new TestParam() { Name = "ProductId", Value = "1" });
testList.Add(new TestParam() { Name = "ProductId", Value = "2" });
testList.Add(new TestParam() { Name = "ProductId", Value = "3" });
testList.Add(new TestParam() { Name = "ProductId", Value = "4" });
testList.Add(new TestParam() { Name = "ProductId", Value = "5" });
testList.Add(new TestParam() { Name = "ProductId", Value = "6" });
testList.Add(new TestParam() { Name = "ProductId", Value = "7" });
testList.Add(new TestParam() { Name = "ProductId", Value = "8" });
TestParam is a normal encapsulated parameter class having a name and a value...
public class TestParam
{
public string Name { get; set; }
public string Value { get; set; }
}
The end result would be a list of lists, having CustomerId 33, with all the rest of the products. The same result would be acquired if I had different names and values in the list of TestParam (the above is just an example).
The following code, ends up with several lists depending on the combinations of the list above...
// First get a list of distinct unique param collections...
List<string> distinctParameterNames = new List<string>();
testList.GroupBy(x => x.Name).ForEach(paramName => {
distinctParameterNames.Add(paramName.Key);
});
// Get counts
List<int> combinationList = new List<int>();
foreach (var x in distinctParameterNames) {
combinationList.Add(testList.Where(y=>y.Name == x).Count());
}
// Will contain 2 lists, one having all combinations of parameters named CustomerId, and another with ProductId combinations...
List<List<TestParam>> parameterList = new List<List<TestParam>>();
foreach (var x in distinctParameterNames) {
// Loop
List<TestParam> parameter = new List<TestParam>();
testList.Where(paramName => paramName.Name == x).ForEach(y =>
{
parameter.Add(new TestParam() { Name = y.Name, Value = y.Value });
});
parameterList.Add(parameter);
}
It would be an intersect between the list, and the end result will be a list of lists, and each list will have the combinations below... So a run would return (in this case) :
Customer 33, Product Id 1
Customer 33, Product Id 2
Customer 33, Product Id 3
Customer 33, Product Id 4
Customer 33, Product Id 5
Customer 33, Product Id 6
Customer 33, Product Id 7
Customer 33, Product Id 8
What would be the most efficient and generic way to do this?
The following is the solution that I was looking for...
public static List<List<T>> AllCombinationsOf<T>(params List<T>[] sets)
{
// need array bounds checking etc for production
var combinations = new List<List<T>>();
// prime the data
foreach (var value in sets[0])
combinations.Add(new List<T> { value });
foreach (var set in sets.Skip(1))
combinations = AddExtraSet(combinations, set);
return combinations;
}
private static List<List<T>> AddExtraSet<T>
(List<List<T>> combinations, List<T> set)
{
var newCombinations = from value in set
from combination in combinations
select new List<T>(combination) { value };
return newCombinations.ToList();
}
Usage (continues with my code snippet of the question itself) :
var intersection = AllCombinationsOf(parameterList.ToArray());
get all the list of customer first like this
var customers = from a in testlist where a.name='customerid'
select a;
var products = from a in testlist where a.name='productid'
select a;
then loop customers
for(var c in customers)
{
loop products
for(var p in products)
{
var customerproducts = new CustomerProducts{
Customer = c.Name +' ' + c.Value
Product = p.Name + ' ' + p.value
};
then add it into a list
}
}
The list needs to be grouped by Name, then it can be joined several times depending on count of groups:
var groups = testList.GroupBy(_ => _.Name);
IEnumerable<IEnumerable<TestParam>> result = null;
foreach (var g in groups)
{
var current = g.Select(_ => new[] { _ });
if (result == null)
{
result = current;
continue;
}
result = result.Join(current, _ => true, _ => true, (actual, c) => actual.Concat(c));
}
// check result
foreach (var i in result)
{
Console.WriteLine(string.Join(", ", i.Select(_ => string.Format("{0}-{1}", _.Name, _.Value))));
}
I have two parameters passed to the method and I need to attach them to the final query list.
(1st parameter)
string[] Price= new string[5];
Price= new string[] { "50", "25", "35" };
(2nd parameter)
List<string> DiscountPrice= new List<string>();
DiscountPrice.Add ("10");
DiscountPrice.Add ("5");
DiscountPrice.Add ("3");
var list= (from d in context.List
where ....
select new MyNewList
{
Name = d.Name,
Country = d.Country,
**Price = ??** //how do I attach the parameters one by one? In the order they were saved?
**DiscountPrice** = ??
}).ToList<MyNewList>();
It sounds like you want to match list elements by index. You can iterate from zero to the number of list elements and access each element by its index:
var prices = new string[] { "50", "25", "35" };
var discountPrices = new List<string>() { "10", "5", "3" };
var items = (from d in context.List
where ....
select new { d.Name, d.Country }).ToList();
var list = (from index in Enumerable.Range(0, items.Count())
select new MyNewList
{
Name = items[index].Name,
Country = items[index].Country,
Price = prices[index],
DiscountPrice = discountPrices[index]
}).ToList();
Another way is to Zip everything together:
var list = items.Zip(prices, (item, price) => new { item, price })
.Zip(discountPrices, (x, discountPrice) => new { x.item, x.price, discountPrice})
.Select(x => new MyNewList
{
Name = x.item.Name,
Country = x.item.Country,
Price = x.price,
DiscountPrice = x.discountPrice
})
.ToList();