Unable to manupulate the list - c#

I am having below class:
public class statistics
{
public int Type { get; set; }
public string Title { get; set; }
public bool Flag { get; set; }
}
Above is list of statistics class so it contains records like this:
1st record : Type = 1
Title = "abc,ttt"
flag= true
2nd Records : Type = 1
Title = "xyz"
flag= true
3rd Records : Type = 1
Title = "lmn,ggg"
flag= true
So here I would like to manipulate my statistics variable and so my
statistics variable should contains 5 records like below:
1st record : Type = 1
Title = "abc"
flag= true
2nd record : Type = 1
Title = "ttt"
flag= true
3rd Records : Type =
Title = "xyz"
flag= true
4th Records : Type = 1
Title = "lmn"
flag= true
5th Records : Type = 1
Title = "ggg"
flag= true
So as you can see that I would like to have a separate record if title contains comma-separated records.
For eg:1st record : Type = 1
Title = "abc,ttt"
flag= true
abc and ttt should split into two records as title contains comma-separated records.
This is how I am trying but unable to do it:
statistics = statistics.Select(o => o.Title.Split(',')).
Select(
t => new statistics
{
Type = t. // not working
}
).ToList();

It seems that you're looking for Split (to turn single comma separated Type into several items) and SelectMany (to flatten the collection):
List<statistics> source = .....;
var result = source
.SelectMany(item => item.Title //split title
.Split(',')
.Select(title => new statistics() {
Type = item.Type,
Title = title,
Flag = item.Flag }))
.ToList(); // finally, materialize into list

You need something like this:
var result = statistics.Select(s => s.Title.Split(',')
.Select(x => new statistics {Type = s.Type, Flag = s.Flag, Title = x}))
.SelectMany(s=>s)
.ToList();
With this output:

var result = statistics.SelectMany(s => s.Title.Split(new char[] { ',' }).
Select(t => new statistics() { Title = t, Flag = s.Flag, Type = s.Type }));

A bit nicer with the query syntax, but don't use the same name statistics for both the class and list:
var result = (from s in statistics
from a in s.Title.Split(',')
select new statistics(){ Type = s.Type, Title = a, Flag = s.Flag }).ToList();

Related

Reuse select new object in linq statement

Below is my linq code and it works. My question is how I can "reuse" those part new ContactResponse, and new AddressResponse in a function to reuse it in another query?
var queryset = (
from a in _repoWrapper.Workshop.FindAll()
where (a.IsActive == true && a.Entity.EntityType.Code == Global.EntityTypeServiceCenterCode)
select new ServiceCenterResponse
{
Id = a.Id,
Name = a.Name,
EntityId = a.EntityId,
Contacts = a.WorkshopContacts.Select(p => new ContactResponse
{
Id = p.Contact.Id,
Type = p.Contact.ContactType.Description,
Code = p.Contact.ContactType.Code,
Value = p.Contact.Value
}).ToList(),
Addresses = a.WorkshopAddresses.Select(p => new AddressResponse
{
Id = p.Address.Id,
AddressType = p.Address.AddressType.Code,
StreetLine1 = p.Address.StreetLine1,
StreetLine2 = p.Address.StreetLine2,
City = p.Address.City,
State = p.Address.State,
PostCode = p.Address.PostCode,
Country = p.Address.Country,
Longitude = p.Address.Longitude,
Latitude = p.Address.Latitude,
Others = p.Address.Others
}).ToList()
}
);
If I correct understand your question, then try this:
Func<WorkshopContact, ContactResponse> contactResponseProjection= p => new ContactResponse
{
Id = p.Contact.Id,
Type = p.Contact.ContactType.Description,
Code = p.Contact.ContactType.Code,
Value = p.Contact.Value
};
And use:
...
Contacts = a.WorkshopContacts.Select(contactResponseProjection).ToList(),
...
Linq has a parameter of type Func in the Select method. This means that you can pass a method to it.
Let me try to do an example.
List<int> list = new List<int> { 1, 2, 3 };
list.Select(AddOne);
where AddOne is a method that you can declare and must have a parameter of type int and a return of whatever you'd like to return. eg.
public int AddOne(int value)
{
return value + 1;
}

Updating property values in one list with a property value average of matching items in another list

I have two Lists and need to update a property value of all the items in the 1st list with a property value average of all the matching items in another list.
class transaction
{
public string orderId;
public string parentOrderId;
public int quantity;
public decimal marketPrice;
public decimal fillPrice;
}
List<transaction> makerTransactions = new List<transaction>()
{
new transaction(){
orderId = "1",
parentOrderId = "1",
quantity = 100,
marketPrice = 75.87M,
fillPrice = 75.87M
}
};
List<transaction> takerTransactions = new List<transaction>()
{
new transaction(){
orderId = "2",
parentOrderId = "1",
quantity = 50,
marketPrice = 75.97M,
fillPrice = 75.97M
},
new transaction(){
orderId = "3",
parentOrderId = "1",
quantity = 50,
marketPrice = 75.85M,
fillPrice = 75.85M
}
};
Trying to make this work with LINQ extension methods but cant figure out the correct way.
makerTransactions.All(mt => mt.fillPrice = takerTransactions
.Where(tt => tt.parentOrderId == mt.orderId)
.Average(ta => ta.fillPrice));
try this:
makerTransactions.ForEach(mt => mt.fillPrice = takerTransactions
.Where(tt => tt.parentOrderId == mt.orderId)
.Average(ta => ta.fillPrice));
All is an extension method. It tells you if all the elements in a collection match a certain condition and, apparently, it's not what you need.
To make it more efficient, first create a dictionary and use that to take the averages from:
var priceDictionary = takerTransactions
.GroupBy(tt => tt.parentOrderId)
.ToDictionary(grp => gr.Key, grp => grp.Average(ta => ta.fillPrice));
makerTransactions.ForEach(mt => mt.fillPrice = priceDictionary[mt.orderId]);

Add duplicates together in List

First question :)
I have a List<Materiau> (where Materiau implements IComparable<Materiau>), and I would like to remove all duplicates and add them together
(if two Materiau is the same (using the comparator), merge it to the first and remove the second from the list)
A Materiau contains an ID and a quantity, when I merge two Materiau using += or +, it keeps the same ID, and the quantity is added
I cannot control the input of the list.
I would like something like this:
List<Materiau> materiaux = getList().mergeDuplicates();
Thank you for your time :)
Check out Linq! Specifically the GroupBy method.
I don't know how familiar you are with sql, but Linq lets you query collections similarly to how sql works.
It's a bit in depth to explain of you are totally unfamiliar, but Code Project has a wonderful example
To sum it up:
Imagine we have this
List<Product> prodList = new List<Product>
{
new Product
{
ID = 1,
Quantity = 1
},
new Product
{
ID = 2,
Quantity = 2
},
new Product
{
ID = 3,
Quantity = 7
},
new Product
{
ID = 4,
Quantity = 3
}
};
and we wanted to group all the duplicate products, and sum their quantities.
We can do this:
var groupedProducts = prodList.GroupBy(item => item.ID)
and then select the values out of the grouping, with the aggregates as needed
var results = groupedProducts.Select( i => new Product
{
ID = i.Key, // this is what we Grouped By above
Quantity = i.Sum(prod => prod.Quantity) // we want to sum up all the quantities in this grouping
});
and boom! we have a list of aggregated products
Lets say you have a class
class Foo
{
public int Id { get; set; }
public int Value { get; set; }
}
and a bunch of them inside a list
var foocollection = new List<Foo> {
new Foo { Id = 1, Value = 1, },
new Foo { Id = 2, Value = 1, },
new Foo { Id = 2, Value = 1, },
};
then you can group them and build the aggregate on each group
var foogrouped = foocollection
.GroupBy( f => f.Id )
.Select( g => new Foo { Id = g.Key, Value = g.Aggregate( 0, ( a, f ) => a + f.Value ) } )
.ToList();
List<Materiau> distinctList = getList().Distinct(EqualityComparer<Materiau>.Default).ToList();

Combine two lists of entities with a condition

Say I have a class defined as
class Object
{
public int ID { get;set; }
public string Property { get; set; }
public override bool Equals(object obj)
{
Object Item = obj as Object;
return Item.ID == this.ID;
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + ID.GetHashCode();
return hash;
}
}
And two lists, defined like so:
List<Object> List1;
List<Object> List2;
These two lists contain objects where ID fields could be the same, but Property fields may or may not. I want to have a result of all objects contained in List1 together with all objects contained in List2, with the condition thatPropertyfield must be set to"1"if it is set to"1"` in any of those lists. The result must contain distinct values (distinct IDs).
For example, if we have 2 lists like this:
List1
-----
ID = 0, Property = "1"
ID = 1, Property = ""
ID = 2, Property = "1"
ID = 3, Property = ""
List2
-----
ID = 1, Property = "1"
ID = 2, Property = ""
ID = 3, Property = ""
I need a result to look like this:
Result
-------
ID = 0, Property = "1"
ID = 1, Property = "1"
ID = 2, Property = "1"
ID = 3, Property = ""
Currently it works like this:
var Result = List1.Except(List2).Concat(List2.Except(List1));
var Intersection = List1.Intersect(List2).ToList();
Intersection.ForEach(x => {
x.Property = List1.Single(y => y.ID == x.ID).Property == "1" ? "1" : List2.Single(y => y.ID == x.ID).Property == "1" ? "1" : "";
});
Result = Result.Concat(Intersection);
...but ForEach is very slow. Can someone suggest a faster way?
var result = List1.Concat(List2)
.GroupBy(o => o.ID)
.Select(g => new Object() {
ID=g.Key,
Property=g.Any(o=>o.Property=="1")?"1":""
})
.ToList();
var result = List1.Concat(List2)
.OrderByDescending(o => o.Property)
.GroupBy(g => o.ID)
.Select(g => g.First())
.ToList();

Group by in linq + select

say I have this data
1 757f27a2-e997-44f8-b2c2-6c0fd6ee2c2f 2 3
2 757f27a2-e997-44f8-b2c2-6c0fd6ee2c2f 3 1
3 757f27a2-e997-44f8-b2c2-6c0fd6ee2c2f 2 2
column 1 // pk
column 2 // userId
column 3 // courseId
column 4 // permissionId
I have this class
class CoursePermissions
{
public string Prefix { get; set; }
public bool OwnerPermission { get; set; } // permissionId 1
public bool AddPermission { get; set; } // permissionId 2
public bool EditPermission { get; set; } // permissionId 3
}
I want to group all the 3 rows by courseId(or Prefix) and then take that information and make a class out Of it
So the end result would be
List<CoursePermissions> permissions = new List<CoursePermissions>();
CoursePermissions a = new CoursePermissions
{
Prefix = "comp101";
OwnerPermission = false,
AddPermission = true,
EditPermission = true
};
CoursePermissions b = new CoursePermissions
{
Prefix = "comp102";
OwnerPermission = true,
AddPermission = false,
EditPermission = false
};
permissions.Add(a);
permissions.Add(b);
So the above is how the object would look if I took all the row data from the db and manually made it the way I wanted it too look. Of course I need to do it somehow as a query.
In my example I have 2 students. They both belong to the same course. Student 1has edit and Add permission for Comp101 but only owner permissions for comp102.
I want to get all the rows back for Comp101 and put it into CoursePermissions. Then I want to get all the rows back for Comp102 and put it into CoursePermissions. Then store all these in a collection and use them.
The only thing I can do is something like this
var list = session.Query<PermissionLevel>().Where(u => u.Student.StudentId == studentId).ToList();
IEnumerable<IGrouping<string, PermissionLevel>> test = list.GroupBy(x => x.Course.Prefix);
foreach (var t in test)
{
CoursePermissions c = new CoursePermissions();
foreach (var permissionLevel in t)
{
if (permissionLevel.PermissionLevelId == 1)
{
c.OwnerPermission = true;
}
}
}
It would nice if I could get rid of the nest for each loop and do it all when the data comes from the query.
Here's an approach that I think is quite functional.
First set up a dictionary of actions that will set the appropriate course permission given a permission level id.
var setPermission = new Dictionary<int, Action<CoursePermissions>>()
{
{ 1, cps => cps.OwnerPermission = true },
{ 2, cps => cps.AddPermission = true },
{ 3, cps => cps.EditPermission = true },
};
Now create a function that will turn the course prefix and a list of permission level ids into a new CoursePermissions object.
Func<string, IEnumerable<int>, CoursePermissions>
buildCoursePermission = (prefix, permissionLevelIds) =>
{
var cps = new CoursePermissions() { Prefix = prefix };
foreach (var permissionLevelId in permissionLevelIds)
{
setPermission[permissionLevelId](cps);
}
return cps;
};
Now all you have left is a simple query that turns your list of permission levels into a list of course permissions.
var coursePermissionsList =
(from pl in list
group pl.PermissionLevelId by pl.Course.Prefix into gcpls
select buildCoursePermission(gcpls.Key, gcpls)).ToList();
How does that work for you?

Categories