Why does this yield an empty set?
Object[] types = {23, 234, "hello", "test", true, 23};
var newTypes = types.Select(x => x.GetType().Name)
.Where(x => x.GetType().Name.Equals("Int32"))
.OrderBy(x => x);
newTypes.Dump();
When you do your select you're getting an IEnumerable<String>. Then you're taking the types of each string in the list (which is all "String") and filtering them out where they aren't equal to "Int32" (which is the entire list). Ergo...the list is empty.
Equals works just fine, it's your query that isn't correct. If you want to select the integers in the list use:
var newTypes = types.Where( x => x.GetType().Name.Equals("Int32") )
.OrderBy( x => x );
Reverse the order of the operations:
var newTypes = types.Where(x => x is int)
.OrderBy(x => x)
.Select(x => x.GetType().Name);
(Notice this also uses a direct type check instead of the rather peculiar .GetType().Name.Equals(…)).
The thing with LINQ is you've got to stop thinking in SQL terms. In SQL we think like this:-
SELECT Stuff
FROM StufF
WHERE Stuff
ORDER BY Stuff
That is what your code looks like. However in LINQ we need to think like this :-
FROM Stuff
WHERE Stuff
SELECT Stuff
ORDER BY Stuff
var newTypes = types.Select(x => x.GetType().Name)
.Where(x => x.Equals("Int32"))
.OrderBy(x => x);
This doesn't work because the Select statement will convert every value in the collection to the name of the underlying type of that value. The resulting collection will contain only string values and hence they won't ever have the name Int32.
Related
The first query returns a list of string, and I am passing them into another table to find corresponding items, but nothing happens. no error message or nothing
var classIds = _contextSpecRepo.Get(x => x.cId.Equals(cId)).Select(x => x.classNames).Distinct().ToList();
// issue happens in the following query
var classes= Repository.Get(x => x.Id.Equals(classIds)).ToList();
The call to Equals, which takes object, hides the problem: you are comparing a single Id to a list of Ids, rather than checking if the Id is present in a collection. This compiles, but yields no result.
Here is how you can fix it:
var classes= Repository.Get(x => classIds.Any(y => y == x.Id)).ToList();
or
var classes= Repository.Get(x => classIds.Contains(x.Id)).ToList();
If you must do it in 2 queries then you have to use contains
var classes= Repository.Get(x => classIds.Contains(x.Id)).ToList();
A better solution would be to use a join on the tables.
you can also skip .ToList()
var classes= Repository.Get(x => classIds.Contains(x.Id));
Using Linq to Objects a query might need to filter based on the result of a function and return the value of that function.
For example
files.Where(x => string.IsNullOrWhiteSpace(x.getProperty(propName)))
.GroupBy(x => x.getProperty(propName));
Does the compiler recognize that the value is going to be required for grouping and keep it?
If it doesn't then there must be a way to select to an anonymous type and query the Where and GroupBy statements against that. Is it possible to do this with an anonymous type?
I am able to declare a class and use that.
class fileSelector
{
internal string prop;
internal myFile file;
}
var groups = files
.Select(x => new fileSelector() { prop = x.getProperty(propName), file = x })
.Where(x => !string.IsNullOrWhiteSpace(x.prop))
.GroupBy(x => x.prop);
But is there a way to do this with an anonymous type?
This is what I tried for an anonymous type
var groups = files.Select(x => new { x.getProperty(propName), x })
.Where(x => !string.IsNullOrWhiteSpace(x.prop))
.GroupBy(x => x.prop);
But this gives the error
Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.
Final answer
var groups = files
.Select(x => new { prop = x.getProperty(propName), file = x })
.Where(x => !string.IsNullOrWhiteSpace(x.prop))
.GroupBy(x => x.prop, x => x.file);
Does the compiler recognize that the value is going to be required for grouping and keep it?
No, since getProperty might have an intended side effect.
If it doesn't then there must be a way to select to an anonymous type and query the Where and GroupBy statements against that. Is it possible to do this with an anonymous type?
Yes. Your code should work as-it-is by just replacing new fileSelector() {...} with new {...}. Note, though, that in your code (and in the modified version using the anonymous type), the elements of the grouping are fileSelector and the anonymous type, not myFile. See Scott Chamberlain's solution for how to fix that.
Alternatively, you could use the let clause to store intermediary values:
var groups = from file in files
let prop = file.getProperty(propName)
where !string.IsNullOrWhiteSpace(prop)
group file by prop;
Does the compiler recognize that the value is going to be required for grouping and keep it?
No, it will touch the value twice.
You where actually quite close with your final example, you can do it with a annonamous type, just give names for each of the members of the anonymous type then add a element selector to make the body of the grouping the file property.
var groups = files
.Select(x => new { prop = x.getProperty(propName), file = x })
.Where(x => !string.IsNullOrWhiteSpace(x.prop))
.GroupBy(x => x.prop, x => x.file);
Given the following values in order :
011124
01112
011123
1905
How could I use Linq to sort this:
List<string> values = new List<string>() { "011124", "01112", "011123", "1905" };
.. so it is effectively this:
List<string> values = new List<string>() { "011124", "011123", "01112", "1905" };
(updated: added '1905' as the previous demo would have sorted easily with an int sort -- sorry for the confusion)
Try this snippet:
var sortedValues = values
.OrderByDescending(x => x.Length)
.ThenByDescending(x => Convert.ToInt32(x));
If you really need use it as a List, then add ToList() at the end.
I'm going to assume you actually care about the value of each integer. If so:
var sortedValues = values.OrderByDescending(x => x.Length)
.ThenByDescending(x => int.Parse(x));
This will yield a deferred IOrderedEnumerable<string>. The int.Parse is only for the purpose of secondary ordering. If you need to materialize it, ToArray or ToList will need to be called.
Most of the answers here are written using Lambda Syntax, if you want query syntax try this. Result should be the same.
var sortedValues = from x in values
orderby x.Length descending, x descending
select x;
values.OrderByDescending(x => x.Length).ThenByDescending(x => int.Parse(x));
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
hi I have the following code and would like to only make each union if a condition is true.
I Know I could write a select of if else but wanted to know if there is a slicker Linq way!?
//join the list into one and sort by seqnumber
SegmentList = Alist.Cast<BaseSegment>()
.Union(BList.Cast<BaseSegment>()).Union(CList.Cast<BaseSegment>())
.OrderBy(item => item.SegSeqNumber).ToList();
So given the above if ATest =true how do I only iclude Alist like wise if BTest && CTest are true how do I include only BList and Clist
Thanks
To do it in a LINQ style way with your checkboxes, something like:
SegmentList = Alist.Where(i => checkbox1.IsChecked).Cast<BaseSegment>()
.Union(BList.Where(i => checkbox2.IsChecked).Cast<BaseSegment>())
.Union(CList.Where(i => checkbox3.IsChecked).Cast<BaseSegment>())
.OrderBy(item => item.SegSeqNumber).ToList();
would work. But I don't think it is either very understandable or efficient.
Something like this?
SegmentList = Alist.Cast<BaseSegment>()
.Union(includeB ? BList.Cast<BaseSegment>() : Enumerable.Empty<BaseSegment>())
.Union(includeC ? CList.Cast<BaseSegment>() : Enumerable.Empty<BaseSegment>())
.OrderBy(item => item.SegSeqNumber)
.ToList();
This is not identical to your original (it will remove duplicates from Alist no matter what) but should be what you want.
For any more than 2 conditional unions, you would probably want a different query, something like:
var listsByCb = new Dictionary<CheckBox, MyListType>
{{ aListBox, aList}, {bListBox, bList}, {cListBox, cList}};
var segmentList = listsByCb.Where(kvp => kvp.Key.Checked)
.SelectMany(kvp => kvp.Value.Cast<BaseSegment>())
.Distinct();
.OrderBy(item => item.SegSeqNumber)
.ToList();
try something like
SegmentList = Alist.Cast<BaseSegment>().Where(z=>ATest)
.Union(BList.Cast<BaseSegment>().Where(x=>Btest)).Union(CList.Cast<BaseSegment>().Where(c=>Ctest))
.OrderBy(item => item.SegSeqNumber).ToList();
where class in each case will return all elements if corresponding test is true and will return no element otherwise. its completely untested though
Use a Where() clause for that, like following:
//join the list into one and sort by seqnumber
SegmentList =
Alist.Cast<BaseSegment>().Where(a => ATest(a))
.Union(
BList.Cast<BaseSegment>(.Where(b => BTest(b))
.Union(CList.Cast<BaseSegment>().Where(c => CTest(c))
.OrderBy(item => item.SegSeqNumber).ToList();
By the way, do you really need the last ToList()?