How can i get items which are equal to string[]? - c#

I have an array of strings
var controlsToGet = new[] {"lblHome","lblContact"};
I have List<LanguageControl> and LanguageControl class holds Controls in it.
I want to get Controls from List which Control.Name == controlsToGet
I am looking for something like that
var all = fooelements.where(l=>l.Control.Name == controlsToGet);
Is it possible with lambda or linq.
Note: I was able to do it with Nhibernate's Disjunction, i am looking for something like that
EDIT : What would i do if i want to use this query for data base with the Entity Framework ?
Thank you

var all = fooelements
.Where(l=>controlsToGet
.Contains(l.Control.Name));
If each item has a list of controls:
var all = fooelements
.SelectMany(l => l.Controls)
.Where(c => controlsToGet
.Contains(c.Name));

You could also use the findall. If microsoft optimise the method you will increase performance, if not it's will be the same as the foreach
var result = fooelements.FindAll(item=> controlsToGet.Contains(item.Name));

If controlsToGet is a string, this will work:
var all = fooelements.SelectMany(l => l.Controls).Where(c => c.Name == controlsToGet);
However, if it's a List<string> you will need:
var all = fooelements.SelectMany(l => l.Controls).Where(c => controlsToGet.Contains(c.Name));

var all = from control in listofcontrols
from toGet in controlsToGet
where toGet == control.name
select control;

A naïve solution such as:
fooelements.SelectMany(l => l.Controls).Where(c => controlsToGet.Contains(c.Name))
is O(n*m) where n is the number of controls, and m is the number of strings in your name array. Some may argue that this is nitpicking and YAGNI and ... (yadda yadda), but what the heck - just a little more code gives you an O(n) solution:
var controlsToGet = new HashSet<string> { "lblHome", "lblContact" };
var controls = fooelements.SelectMany(l => l.Controls)
.Where(c => controlsToGet.Contains(c.Name))

Related

C# Linq union multiple properties to one list

Basically I have an object with 2 different properties, both int and I want to get one list with all values from both properties. As of now I have a couple of linq queries to do this for me, but I am wondering if this could be simplified somehow -
var componentsWithDynamicApis = result
.Components
.Where(c => c.DynamicApiChoicesId.HasValue ||
c.DynamicApiSubmissionsId.HasValue);
var choiceApis = componentsWithDynamicApis
.Select(c => c.DynamicApiChoicesId.Value);
var submissionApis = componentsWithDynamicApis
.Select(c => c.DynamicApiSubmissionsId.Value);
var dynamicApiIds = choiceApis
.Union(submissionApis)
.Distinct();
Not every component will have both Choices and Submissions.
By simplify, I assume you want to combine into fewer statements. You can also simplify in terms of execution by reducing the number of times you iterate the collection (the current code does it 3 times).
One way is to use a generator function (assuming the type of items in your result.Components collection is Component):
IEnumerable<int> GetIds(IEnumerable<Component> components)
{
foreach (var component in components)
{
if (component.DynamicApiChoicesId.HasValue) yield return component.DynamicApiChoicesId.Value;
if (component.DynamicApiSubmissionsId.HasValue) yield return component.DynamicApiSubmissionsId.Value;
}
}
Another option is to use SelectMany. The trick there is to create a temporary enumerable holding the appropriate values of DynamicApiChoicesId and DynamicApiSubmissionsId. I can't think of a one-liner for this, but here is one option:
var dynamicApiIds = result
.Components
.SelectMany(c => {
var temp = new List<int>();
if (c.DynamicApiChoicesId.HasValue) temp.Add(c.DynamicApiChoicesId.Value);
if (c.DynamicApiSubmissionsId.HasValue) temp.Add(c.DynamicApiSubmissionsId.Value);
return temp;
})
.Distinct();
#Eldar's answer gave me an idea for an improvement on option #2:
var dynamicApiIds = result
.Components
.SelectMany(c => new[] { c.DynamicApiChoicesId, c.DynamicApiSubmissionsId })
.Where(c => c.HasValue)
.Select(c => c.Value)
.Distinct();
Similar to some of the other answers, but I think this covers all your bases with a very minimal amount of code.
var dynamicApiIds = result.Components
.SelectMany(c => new[] { c.DynamicApiChoicesId, c.DynamicApiSubmissionsId}) // combine
.OfType<int>() // remove nulls
.Distinct();
To map each element in the source list onto more than one element on the destination list, you can use SelectMany.
var combined = componentsWithDynamicApis
.SelectMany(x => new[] { x.DynamicApiChoicesId.Value, x.DynamicApiSubmissionsId.Value })
.Distinct();
I have not tested it but you can use SelectMany with filtering out the null values like below :
var componentsWithDynamicApis = result
.Components
.Select(r=> new [] {r.DynamicApiChoicesId,r.DynamicApiSubmissionsId})
.SelectMany(r=> r.Where(p=> p!=null).Cast<int>()).Distinct();

How to avoid two embedded cycles in linq query C#

var listOfIds = new List<string>();
var allItems = IEnumerable<Info>();
foreach (var id in collectionIds)
{
listOfIds.AddRange(allItems
.Where(p => p.Data.FirstOrDefault(m => m.Key == "myId").Value == id)
.Select(x => x.Id));
}
I would like to avoid using AddRange but use only Add in this case and maybe use only FirstOrDefault in the place of where to avoid the last Select case.
Is this possible and if yes how?
Assuming your original code is giving you the correct data, specifically you are OK with:
Only concerned that the first item in p.Data contains a matching value and;
p.Data will always contains at least a single element.
Then this code will give you the same output:
var listOfIds = allItems
.Where(p => collectionIds.Contains(p.Data.First(m => m.Key == "myId").Value))
.ToList();
However, if you really do care that any value in p.Data matches, then this would be more appropriate:
var listOfIds = allItems
.Where(p => p.Data.Any(m => m.Key == "myId" &&
collectionIds.Contains(m.Value)))
.ToList();
How about this approach:
var listOfIds = new List<string>();
var allItems = IEnumerable<Info>();
var groupedAllItems = allItems.GroupBy(x => x.Data.FirstOrDefault(m => m.Key == "myId")?.Value ?? "MyIdNotFound");
//collectionIds should be of type HashSet<string> for the contains to be fast
listOfIds.AddRange(groupedAllItems.Where(x => collectionIds.Contains(x.Key)).SelectMany(x => x));

how to do a contains on an array of strings?

I am using a predicate builder class and I need to invoke the contains method on an array of strings so in the code below instead of radio I would be passing in an array of strings:
wherePredicate = wherePredicate.Or(m => m.MediaType.Contains("Radio"));
the full code section:
if (param.iMediaGroupID > 0)
{
var wherePredicate = PredicateBuilder.False<MediaChannelModel>();
var ss = new NeptuneRepository<Lookup_MediaTypes>();
var mediagroups = ss.FindWhere(m => m.MediaGroupID == param.iMediaGroupID).Select(m => m.Name);
//problem area
wherePredicate = wherePredicate.Or(m => mediagroups.Contains(m.MediaType));
predicate = predicate.And(wherePredicate);
}
mediaGroups is: ["Radio","Tv","Magazine"]
If m.MediaType is any of these values then the predicate is true.
Is there a way to do this in C#?
I suspect you want something like:
wherePredicate = wherePredicate.Or(m => array.Contains(m.MediaType));
Or perhaps:
wherePredicate = wherePredicate.Or(m => array.Any(x => m.MediaType.Contains(x)));
If neither of those are what you're after, please clarify your requirements.
EDIT: The problem you're now facing is that you're not actually asking whether an array contains the value. You're asking whether a query contains a value. If you change it to an actual array, you may well find it works:
var mediagroups = ss.FindWhere(m => m.MediaGroupID == param.iMediaGroupID)
.Select(m => m.Name)
.ToArray();
However, if these are querying the same database, you'd be better off trying to do this in some kind of join.
Jon Skeet's answer worked perfectly for me. I had been struggling to make the .Contains search for a substring in a string array against the database, rather than try to find a substring in a single C# string object. Thank you!
Here's the modified code that worked for me:
var predicate = PredicateBuilder.False<ClientXMemberDetail>();
predicate = predicate.Or(x => strArrselectedCustomMemberNumbers.Any<string>(y => x.MemberID.Contains(y)));
CustomSearchMembersAlreadyMatched = ClientXContext.ClientXMemberDetails
.AsExpandable()
.Where(predicate)
.ToList()
.Select(r => r.MemberID.ToString()).ToList();
(ClientXContext above is an instance of the ObjectContext class, strArrselectedCustomMemberNumbers is a string array, ClientXMemberDetails is ObjectSet, where ClientXMemberDetail is the EntityObject)
Edit: Anonymized my client's name

Modifying an IEnumerable type

I have a a string IEnumerable type that I get from the below code.The var groups is an Enumerable type which has some string values. Say there are 4 values in groups and in the second position the value is just empty string "" .The question is how can I move it to the 4th ie the end position.I do not want to sort or change any order.Just move the empty "" value whereever it occurs to the last position.
List<Item> Items = somefunction();
var groups = Items.Select(g => g.Category).Distinct();
Simply order the results by their string value:
List<Item> Items = somefunction();
var groups = Items.Select(g => g.Category).Distinct().OrderByDescending(s => s);
Edit (following OP edit):
List<Item> Items = somefunction();
var groups = Items.Select(g => g.Category).Distinct();
groups = groups.Where(s => !String.IsNullOrEmpty(s))
.Concat(groups.Where(s => String.IsNullOrEmpty(s)));
You can't directly modify the IEnumerable<> instance, but you can create a new one:
var list = groups.Where(x => x != "").Concat(groups.Where(x => x == ""));
Note that in this query, groups is iterated twice. This is usually not a good practice for a deferred IEnumerable<>, so you should call ToList() after the Distinct() to eagerly evaluate your LINQ query:
var groups = Items.Select(g => g.Category).Distinct().ToList();
EDIT :
On second thought, there's a much easier way to do this:
var groups = Items.Select(g => g.Category).Distinct().OrderBy(x => x == "");
Note that this doesn't touch the order of the non-empty elements since OrderBy is stable.
var groups = Items.Select(g => g.Category).Distinct().OrderByDescending(s =>s);
I don't like my query but it should do the job. It selects all items which are not empty and unions it with the items which are empty.
var groups = Items.Select(g => g.Category).Distinct()
.Where(s => !string.IsNullOrEmpty(s))
.Union(Items.Select(g => g.Category).Distinct()
.Where(s => string.IsNullOrEmpty(s)));
Try something like
var temp = groups.Where(item => ! String.IsNullOrEmpty(item)).ToList<string>();
while (temp.Count < groups.Count) temp.Add("");

best way to receive a list of objects from list of KeyValuePair?

I have a list of KeyValuePairs and I want to filter it based on the key value so eventually I will get a list of values which is filtered (meaning - will not contain all the values that were in the original list).
I guess maybe the best way is some form of Lambda expression but I am not sure how to achieve it.
Thanks,
Alon
Try this:
var values = list.Where(x => x.Key == "whatever").Select(x => x.Value);
This will give you a filtered list of the values only.
Obviously you can change the way you filter your keys.
Use the following:
var filteredList = list.Where(x => x.Key == "Key");
What you're looking for some combination of LINQ extension methods (which depends on what you're exactly trying to do).
For example if I had a List of fruits to their colors and wanted to get a collection of which fruits are red, I would do something like:
var fruits = new List<KeyValuePair<string,string>>() {
new KeyValuePair<string,string>("Apple", "Green"),
new KeyValuePair<string,string>("Orange", "Orange"),
new KeyValuePair<string,string>("Strawberry", "Red"),
new KeyValuePair<string,string>("Cherry", "Red")
};
var redFruits = fruits.Where(kvp => kvp.Value == "Red").Select(kvp => kvp.Key);
// this would result in a IEnumberable<string> { "Strawberry", "Cherry" }

Categories