How do I use an IEnumerable string parameter in my code? - c#

As part of my search page I am allowing the user to make a multi-select on specific search terms using the KendoUI Multiselect widget. These items in the collection are passed to my controller as a parameter. My question is, after I have passed them to my controller how do I use them? more specifically how do I use them in my Where statement which uses the Contains method.
Here is my code for the multi select widget
#(Html.Kendo().MultiSelect()
.Name("vessel_type")
.Placeholder("Select Type")
.BindTo(new List<string>() {
"AHTS",
"PSV",
"Special"
}))
Here is my controller code which uses the vessel_type as a parameter
public ActionResult Search(IEnumerable<string> vessel_type)
{
var vessels = (from o in db.vessels
select o);
vessels = vessels.Where(s => s.vessel_type.Contains(vessel_type));
return PartialView("_results", vessels);
}
This line isn't correct because it's expecting a string but I have a collection of mroe than one:
vessels = vessels.Where(s => s.vessel_type.Contains(vessel_type));
Thanks

If I understand the question correctly, I believe you need to perform the check the other way around, that is check if the vessel_type collection contains the vessel's type:
vessels = vessels.Where(s => vessel_type.Contains(s.vessel_type));
Here Contains is an extension method on IEnumerable<T>.
On a side note, since the parameter represents a collection, I think a plural name is more appropriate, for example vessel_types.

Related

using lambda, how to apply an existing function to all elements of a list?

Excuse me, a quick question:
I have a list of strings, string are full paths of some files. I would like to get only the filename without the path neither the extension for each string (and to understand lambda more)
Based on the lambda expression in How to bind a List to a DataGridView control? I am trying something like the below:
FilesName = Directory.GetFiles(fbd.SelectedPath).ToList(); // full path
List<string> FilesNameWithoutPath = AllVideosFileNames.ForEach(x => Path.GetFileNameWithoutExtension(x)); // I want only the filename
AllVideosGrid.DataSource = FilesNameWithoutPath.ConvertAll(x => new { Value = x }); // to then bind it with the grid
The error is:
Can not convert void() to List of string
So I want to apply Path.GetFileNameWithoutExtension() for each string in FilesName. And would appreciate any extra description on how Lamba works in this case.
ForEach will execute some code on each item in your list, but will not return anything (see: List<T>.ForEach Method). What you want to do is Select the result of the method (see: Enumerable.Select<TSource, TResult> Method), which would look something like:
List<string> FilesNameWithoutPath = AllVideosFileNames
.Select(x => Path.GetFileNameWithoutExtension(x))
.ToList();
You are using List<T>.ForEach method which takes each element in the list and applies the given function to them, but it doesn't return anything. So what you are doing basically is getting each file name and throwing them away.
What you need is a Select instead of ForEach:
var fileNamesWithoutPath = AllVideosFileNames
.Select(x => Path.GetFileNameWithoutExtension(x))
.ToList();
AllVideosGrid.DataSource = fileNamesWithoutPath;
This will project each item, apply Path.GetFileNameWithoutExtension to them and return the result, then you put that result into a list by ToList.
Note that you can also shorten the Select using a method group without declaring a lambda variable:
.Select(Path.GetFileNameWithoutExtension)

C# FindAll Method with array of values

I have some kind of ULR object list like below
List<myUrl> urls = new List<myUrl>();
myUrl is a class that contains 4 field and all of them string.
in this list I want to find items does not contain another given list of values
List<myUrl> result = urls.FindAll(
different_from_list_of_values condition;
);
how can I do that?
the best way should be topic, the idea is to override Equals method with your own logic, it have example for two fields class TwoDPoint.
Then you just need to check this topic
instead of p2.ID == p.ID => you would rather have p2.Equals(p)

Handling lambda expression with generic property name

Working with EPiServer Find and trying to build a generic facet funcionality for it to simplify managing which facet should be enabled. I would like to construct two generic methods, one for adding active filters to perform the hits search and one to perform the available facet filters remaining.
The first method will perform the following (specific code for brand filter):
var brandFilter = client.BuildFilter<FashionProduct>();
foreach (var facet in SelectedGroup.Facets.Where(x => x.Selected))
{
brandFilter = brandFilter.Or(x => x.Brand.Match(facet.Key));
}
query = query.Filter(brandFilter);
I would like to be able to call it in a generic way so I could base the available facets on some simple list of strings or objects. Like this:
query = AddFilterFacet<FashionProduct>(query, "Brand", SelectedGroup.Facets)
So the method would take the type of object to filter on, the query to append the filters on, the name of the property to filter on and the list of values to add.
The second method is similar but relates more to perform the following:
facetQuery = facetQuery.TermsFacetFor(x => x.Brand)
...
var brandFacets = facetResult.TermsFacetFor(x => x.Brand).Terms;
Is it possible to build this kind of functionality? The biggest questionmark I have is how to translate the "Brand" input string to be the Brand Property in x => x.Brand
private void AddFilterFacet<T>(IClient client, ref ITypeSearch<T> query, string propertyName, List<FacetOption> facets)
{
var filter = client.BuildFilter<T>();
foreach (var facet in facets)
{
filter = filter.Or(x => x.????.Match(facet.Key));
}
query = query.Filter(filter);
}
The .Or method takes a
System.Linq.Expressions.Expression<Func<T, Find.Api.Querying.Filter>>
so perhaps something can be used to make a proper generic call to it
It's definitely possible to create generic lambda expressions, it's just not easy and requires a lot of reflection code.
I haven't done it in a while, but maybe if you look at the code i created for something similar a while ago (Generic lambda expressions) it'll help. I'm sure someone with fresher experience will help you out here soon enough.
Decimal precision attribute <-- take a look a this answer witch has code to genereate modelBuilder.Entity<CLASS>().Property(OBJECT=> OBJECT.PROPERTY).HasPrecision(12, 10) automatically from an attribute in a class

List<string> passed in ViewBag gets spoiled with "System.Collections.Generic.List`1[System.String]"

I think I have a very noob question but there it is:
I have a link to another controller and I want to pass a list of strings.
Controller:
[HttpPost]
public ActionResult Index(DateTime? dateFilter, int? Provider, int? middleOffice)
{
...
ViewBag.ReasonGroupName = new List<string>() { "Faults" };
...
}
View:
#Html.ActionLink(item.username, "Edit", "Hours", new { IdUser = item.IdUser, ReasonGroupNames = (List<string>)ViewBag.ReasonGroupName }, new { #class = "iframeFull" })
In my controller the ViewBag.ReasonGroupName is created properly: a list with the index 0 item ([0]) with the string "Faults"but when I receive it in my other controller the 0 index element of my list comes with "System.Collections.Generic.List1[System.String]" instead of "Faults"
I also try changing ViewBag for ViewData object but same problem appear.
Any ideia on what I'm doing wrong?
Thanks
The text "System.Collections.Generic.List1[System.String]" is what you get if you call ToString() on an instance of the List type. When you generate the link the url has to be a string, hence ToString() is called on each argument.
It is not clear what information you are trying to send via the action link. If you want the first value then you could change the link to be the following:
#Html.ActionLink(item.username, "Edit", "Hours", new { IdUser = item.IdUser, ReasonGroupNames = ((List<string>)ViewBag.ReasonGroupName)[0] }, new { #class = "iframeFull" })
This will set the ReasonGroupName to the first value in the list.
But you probably want to send all the values in the list and not just the first. In that case you could join all the values of the list into a string and pass that as the argument instead.
So in your controller you could do:
ViewBag.ReasonGroupName = string.Join(",", yourListVariableHere);
This will convert the list into a comma separated string that can be included in the url properly.
The #Html.ActionLink() method is used generate a url link to the given Controller/Action. Thus, it can only contain parameters that can be passed in the url of the link.
So I don't think you can pass an object through on the url.
You are trying to send through get-request some complex data. As first, that is bad idea anyway, because in one good moment you can overcome limit for link.
You have solutions:
Stay on sending get-request: serialize list to string - use special serializers or use string.Join(",", list); your edit method must accept string, which you deserialize to List by special deserializer or by string.Split method. Using of string.Join and string.Split is bad idea if your strings can contain char ','
Don't send data. You can store it in db/session and then send id of saved data. You must be accurately: you must delete old data from db/session, but not new data
Use post-request. Make form with #Html.BeginForm(...); in form with for add hidden input (#Html.HiddenFor(...)), and add submit button <input type="submit">, style it as link.
Your problem is due to the fact that you're trying to pass a List of strings but have assigned it to a string property in the ActionLink(). You'll need to create a small helper to explode this List out to a single formatted string of your choice. This will then be passed to the url as a (for example) comma separated string. You'd then do some magic in the controller action to rehydrate this as required back to the List.

C# Lists: How to copy elements from one list to another, but only certain properties

So I have a list of objects with a number of properties. Among these properties are name and id. Let's call this object ExtendedObject. I've also declared a new List of different objects that have only the properties name and id. Let's call this object BasicObject.
What I'd like to do is convert or copy (for lack of better words) the List of ExtendedObject objects to a list of BasicObject objects. I know C# Lists have a lot of interesting methods that can be useful, so I wondered if there were an easy way to say something to the effect of:
basicObjectList = extendedObjectList.SomeListMethod<BasicObject>(some condition here);
But I realize it may end up looking nothing like that. I also realize that I could just loop through the list of ExtendedObjects, create a new BasicObject from each ExtendedObject's name and id, and push it onto a list of BasicObjects. But I was hoping for something a little more elegant than that.
Does anyone have any ideas? Thanks very much.
It depends on exactly how you'd construct your BasicObject from an ExtendedObject, but you could probably use the ConvertAll method:
List<BasicObject> basicObjectList =
extendedObjectList.ConvertAll(x => new BasicObject
{
id = x.id,
name = x.name
});
Or, if you prefer, you could use the LINQ Select method and then convert back to a list:
List<BasicObject> basicObjectList =
extendedObjectList.Select(x => new BasicObject
{
id = x.id,
name = x.name
}).ToList();
if you are on .NET 3.5 or greater this could be done by using LINQ projections:
basicObjectList = extendedObjectList.Select(x => new BasicObject { Id=x.Id, Name=x.Name})
var basicObjectList = extendedObjectList.Select(eo => new BasicObject { name = eo.name, id = eo.id });
I think that the OP's suggestion of "BasicObject" was just a pseudonym for a resulting object with a specific subset of properties from the original set. Anonymous types are your friend (as indicated by #mumtaz).
Assuming the following extendedObjectList if of IEnumerable<T> (including a List):
// "var" used so that runtime infers the type automatically
var subset = extendedObjectList
// you can use any Linq based clause for filtering
.Where(a => <conditions>)
// where the runtime creates a runtime anonymous type to describe your "BasicObject"
.Select(a => new { a.Property1, a.Property2, a.Property3 })
// conversion to a List collection of your anonymous type
.ToList();
At this point, subset contains a List of an anonymous (runtime) type that contains three properties - Property1, Property2, Property3.
You can manipulate this resulting list as follows:
// execute an anonymous delegate (method) for each of the new anonymous objects
subset.ForEach
(
basicObject =>
{
Console.WriteLine("Property1 - {0}", basicObject.Property1);
Console.WriteLine("Property2 - {0}", basicObject.Property2);
Console.WriteLine("Property3 - {0}", basicObject.Property3);
}
);
// grab the first object off the list
var firstBasicObject = subset.First();
// sort the anonymously typed list
var sortedSubset = subset.OrderBy(a => a.Property1).ToList();
Once the runtime has resolved the new object (of any combination of properties from the source object), you can use it virtually any way that you wish.
For Linq-to-Sql applications (using IQueryable<T>), the Select statement can be used to obtain specific column data (instead of the entire row), thereby creating an anonymous type to describe a subset of column data for a given row.

Categories