Extension method on Item in List within another List [duplicate] - c#

This question already has answers here:
Flatten List in LINQ
(5 answers)
Closed 6 years ago.
I'm trying to perform an run an extension method on every item within a List<> that is inside another List<> to return collection of a given type (returned by the extension method).
I initially tried (and failed) to do this using linq, but I have the following:
var dataset = GetReportDataset(org);
var reportData = new List<InterventionAndNeetRiskReportLineModel>();
foreach (var record in dataset)
{
foreach (var inter in record.InterventionHistory)
{
reportData.Add(inter.ToInterventionAndNeetRiskReportLineModel());
}
}
return _reportWriter.ReportCsvStream(reportData);
So my question is, how can I project the result of my extension method for each item in the child collection using linq?
UPDATE ToInterventionAndNeetRiskReportLineModel() extension method
public static InterventionAndNeetRiskReportLineModel ToInterventionAndNeetRiskReportLineModel(this InterventionHistory intervention)
{
return new InterventionAndNeetRiskReportLineModel()
{
Beneficiary = intervention.Person.Beneficiary,
CourseTitle = intervention.CourseTitle,
CaseNotes = intervention.CaseNotes,
EndDate = intervention.EndDate?.ToString(),
StartDate = intervention.StartDate.ToString(),
ParticipantId = intervention.Person.ParticipantId,
FirstEit = intervention.Person.EitScores.GetFirstEitReading().ToString(),
LastEit = intervention.Person.EitScores.GetLastEitReading().ToString(),
FirstLpt = intervention.Person.LptScores.GetFirstLptReading().ToString(),
LastLpt = intervention.Person.LptScores.GetLastLptReading().ToString(),
Gender = intervention.Person.Equalitites.Gender,
HoursAttended = intervention.NoOfHours.ToString(),
LanguageOfDelivery = intervention.DeliveryLanguage,
Providername = intervention.ProviderName,
QanCode = intervention.QanCode,
SchoolCollegeName = intervention.ProviderName
};
}

I'm not completely sure which part of the question code you want to separate into an extension method. Also, don't be to focused on the extension method part, its nothing different from other functions, as far as the writing is concerned.
You can use SelectMany in order to get a flat list of InterventionHistory objects and Select in order to convert to InterventionAndNeetRiskReportLineModel and ToList for the final result as list instead of IEnumerable<T> if you really need that.
var reportData = GetReportDataset(org)
.SelectMany(r => r.InterventionHistory)
.Select(i => i.ToInterventionAndNeetRiskReportLineModel())
.ToList();
So, maybe you want an extension method like
public static IEnumerable<InterventionAndNeetRiskReportLineModel> ToInterventionRiskReports(this IEnumerable<ReportDataset> _self)
return _self
.SelectMany(r => r.InterventionHistory)
.Select(i => i.ToInterventionAndNeetRiskReportLineModel());
}
And use it as
var reportData = GetReportDataset(org).ToInterventionRiskReports().ToList();
... as I said, its not completely clear, which part you want to refactor into an extension method.

Related

Is there ToSortedDictionary in Linq? [duplicate]

This question already has answers here:
Building a sorted dictionary using ToDictionary
(3 answers)
Closed 5 years ago.
I am refactoring a code like this:
var result = new SortedDictionary<int, string>();
foreach (var item in foo)
{
result[item.id] = item.name;
}
foreach (var item in bar)
{
result[item.id] = item.name;
}
return result;
I would like to write:
return foo.Concat(bar).ToSortedDictionary(i => i.id, i => i.name);
but I did not find it in Linq?
First solution is to prepare Dictionaryand use as an argument to create a SortedDictionary:
return new SortedDictionary<T1, T2>(foo.Concat(bar).ToDictionary())
Another option is to write an extension method like this:
public static SortedDictionary<K, V> ToSortedDictionary<K,V>(this Dictionary<K, V> existing)
{
return new SortedDictionary<K, V>(existing);
}
You can use return new SortedDictionary<T1, T2>(foo.Concat(bar).ToDictionary()); or you can write your own extension method.
As to why it doesn't exist, there's plenty of similar methods that don't exist- the basics are provided and whatever else you need you can easily make yourself.

update a List<Object> with LINQ [duplicate]

This question already has answers here:
Update all objects in a collection using LINQ
(18 answers)
Closed 7 years ago.
I am trying to add a SELECT ALL functionality to my grid with LINQ, but I get a compilation error.
List<Person_> PeopleUpdated = People.ToList().ForEach(a => a.SELECTED = true).ToList();
It says
Cannot implicitly convert type 'void' to
'System.Collections.Generic.List <
LogoSapHrIntegration.frmXmlUpload.Person_>'
what am I doing wrong?
The List<T>.ForEach has no return value (ie void), so you can't run ToList() against that. (see MSDN)
ForEach a specific action for each item in the list (just like doing a real for loop).
In your case a simple for loop to select all is most efficient.
foreach (var person in People)
person.Selected = true
List<T>.ForEach returns void (in your case, it changes your collection in place). ForEach takes an Action<T> and executes that on each item of your list.
See List(T).ForEach on MSDN
The ForEeach method (which is not LINQ) runs an action on each item in the list, it's not used to filter out items from a list so it doesn't return a result.
Just run the method on each item; there is no result to assign:
People.ToList().ForEach(a => a.SELECTED = true);
If you wanted a new list of items where the property was changed, you would need to clone the items to make them separate from the originals:
List<Person_> PeopleUpdated = People.ToList().Select(a => {
Person_ b = a.Clone();
b.SELECTED = true;
return b;
}).ToList();
(If the class doesn't support cloning, you would need to implement the Clone method (and preferably the IClonable interface).)
First of all, you can use a normal foreach loop:
foreach (var person in people)
{
person.Selected = true;
}
Which is the simplest and cleanest.
If you really want to jump to hoops and use LINQ, you can use ConvertAll:
var list = new List<Person> { new Person(), new Person() };
var convertedPeople = list.ConvertAll(person =>
{
person.Selected = true;
return person;
});

Dynamic table name in linq [duplicate]

This question already has answers here:
Querying data using Entity Framework from dynamically created table
(2 answers)
Dynamically set the table name in LINQ query
(3 answers)
Closed 8 years ago.
I'm trying to execute some LINQ commands using a dynamic table name. For example, instead of:
var o = (from x in context.users select x);
I want to use something like:
var o = (from x in getTableObjectByName("users", context) select x);
More or less. Here's the code I have so far, which both compiles and runs:
using (MySiteEntities ipe2 = new MySiteEntities()) {
var propinfo1 = Type.GetType("MySiteNamespace.MySiteEntities").GetProperty("users");
var propval1 = propinfo1.GetValue(ipe2, null);
}
That runs, but always returns zero records. The users table most definitely contains records, and in any case when I call it directly using the first method above I get all of the records as expected. How can I modify my code to actually pull down records, rather than just an empty collection?
Edit: I've also tried this:
using (MySiteEntities ipe = new MySiteEntities())
{
var prop = Type.GetType("MySiteNamespace.MySiteEntities").GetProperty("users");
Type dbsetType = typeof(DbSet<>);
dbsetType = dbsetType.MakeGenericType(Type.GetType("MySiteNamespace.user"));
Type t = dbsetType.GetType();
var val = prop.GetValue(ipe, null);
}
In this case, the code not only runs, but actually returns the results as expected. However, val is an Object. I need to cast it to the type DbSet<user>, which would be easy enough, except that the parameter user is only known at runtime....the cast needs to be dynamic as well. I've tried using Convert.ChangeType(val, t);, but that throws an
InvalidCastException (Object must implement IConvertible).
How can I convert the val variable to an actually usable object?
No idea if this is relevant, but this is on EntityFramework 4.
In your DbContext class, add a method say called Set that returns:
public DbSet Set(string name)
{
// you may need to fill in the namespace of your context
return base.Set(Type.GetType(name));
}
Which you can query like this:
using (var db = new YourDataContext())
{
// Since your DbSet isn't generic, you can can't use this:
// db.Set("Namespace.EntityName").AsQueryable().Where(a=> a.HasSomeValue...
// Your queries should also be string based.
// Use the System.Linq.Dynamic nuget package/namespace
var results = db.Set("Namespace.EntityName")
.AsQueryable()
.Where("SomeProperty > #1 and SomeThing < #2", aValue, anotherValue);
// you can now iterate over the results collection of objects
}
More information on System.Linq.Dynamic can be found here

Lambda Statements in Linq

Is it possible to do something like the following in Linq?
List<Group> groupsCollection = new List<Group>();
groups.Select( g => {
String id = g.Attributes["id"].Value;
String title = g.Attributes["title"].Value;
groupsCollection.Add( new Group(id, title) );
} );
This is just an example. I know a Foreach loop would be sufficient but I was querying whether it would be possible or not.
I tried it, and it's saying:
Cannot convert lambda expression to type System.Collections.IEnumerable<CsQuery.IDomObject> because it's not a delegate type
Edit: Group is my custom class. Groups are a collection of CsQuery Dom Objects. You can think of them as a collection of html anchor elements. They are IEnumerable.
I think you're looking for this:
groupsCollection = groups.Select(g =>
new Group(g.Attributes["id"].Value,
g.Attributes["title"].Value)).ToList();
Explanation:
Select() projects each element of a sequence into a new form. (From MSDN)
It's basically a transformation function that takes one IEnumerable and transforms it, in whatever way you like, to another IEnumerable, which seems to be exactly what you want here.
Select() takes a lambda expression that returns a value; it shouldn't have any sife effects.
You want
var groupsCollection = groups.Select(g => {
return ...;
}).ToList();
You can use the constructor to initialize it with the query:
var query = groups.Select( g => {
String id = g.Attributes["id"].Value;
String title = g.Attributes["title"].Value;
return new Group(id, title);
});
List<Group> groupsCollection = new List<Group>(query);
Do not modify colections in a linq-query, instead select data that can be used to modify collections.
As you said, you know using ForEach would be relevant. However you are just curious if you can do it another way. As others pointed out, you can use Select but the code in the {...} should return some value. However, you can do it this way which is better if you want to stop or break the loop depending on some condition using TakeWhile:
groups.TakeWhile( g => {
String id = g.Attributes["id"].Value;
String title = g.Attributes["title"].Value;
groupsCollection.Add( new Group(id, title) );
return true;//If you want to break the loop, just return false accordingly.
});

Converting IList<string> to List<string>() [duplicate]

This question already has answers here:
Cast IList to List
(9 answers)
Closed 9 years ago.
I have a function that takes IList<string> someVariable as a parameter. I want to convert this to a list so I can sort the values alphabetically.
How do I achieve this?
you can just do
var list = new List<string>(myIList);
list.Sort();
or
var list = myIList as List<string>;
if (list != null) list.Sort; // ...
IList<string> someVariable = GetIList();
List<string> list = someVariable.OrderBy(x => x).ToList();
IList implements IEnumerable. Use the .ToList() method.
var newList = myIList.ToList();
newList.Sort();
You can use linq to sort an IList<string> like so:
IList<string> foo = .... ; // something
var result = foo.OrderBy(x => x);
You don't have to convert to a List to sort things. Does your sorting method require a List but not accept an IList. I would think this is the actual problem.
Furthermore if you really need a List, if your IList realy is a List (which is somewhat likely) you can just interpret it as such. So I would first check if it is already a List before creating a new one
var concreteList = parameter as List<T> ?? parameter.ToList();

Categories