I have the following Linq Expression
var tooDeep = shoppers
.Where(x => x.Cart.CartSuppliers.First().Name == "Supplier1")
.ToList();
I need to turn the name part into the following string.
x.Cart.CartSuppliers.Name
As part of this I turned the Expression into a string and then split on the . and removed the First() argument. However, when I get to CartSuppliers this returns a Suppliers[] array. Is there a way to get the single type from this. eg. I need to get a Supplier back.
Update: Got it to work
var fullName = m.ToString().Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
// this supports the "deep graph" name - "Product.Address.City"
var fixedName = fullName.Skip(1).Take(fullName.Length - 1)
.Where(x => x != "First()")
.Select(x => System.Text.RegularExpressions.Regex.Replace(x, #"\[[0-9]+\]",""))
.ToArray();
with this:
var prop = property.PropertyType.HasElementType ? property.PropertyType.GetElementType() property.PropertyType;
which enabled be to find the individual type from an array.
Thanks
firstSupplierWithNeededName = shoppers
.SelectMany(s => s.Cart.CartSuppliers)
.First(s => s.Name == "Supplier1");
But also look into using FirstOrDefault or Single if it has to return just one.
Related
I have a problem that I can't fix with my rather small LINQ knowledge.
I have a collection of lists that contain a list of fields.
I need only the lists with the properties hidden == false and that got fields with the description "Special Field".
I tried the following approaches... none of them worked:
clientContext.Load(listCollection,
lists => lists
.Where(list => list.Hidden == false)
.SelectMany(list => list.Fields)
.Where(field => field.Description == "Special Field"));
and
var listQuery = from list in listCollection.Where(l => l.Hidden == false)
from field in list.Fields
where field.Description == "Special Field"
select list;
and
var listQuery2 = listCollection
.SelectMany(lists => listCollection)
.Where(l => l.Hidden == false)
.SelectMany(fields => fields.Fields)
.Where(f => f.Description == "Special Field"
all followed by
var result = clientContext.LoadQuery(listQuery2);
clientContext.ExecuteQuery();
None of them worked.
I get the following exception (for the last query but message is similar to the other querys):
The query expression 'value(Microsoft.SharePoint.Client.ListCollection).SelectMany(lists => value(docma.Library.Classes.SharepointDataConnector+<>c__DisplayClass56_0).listCollection).Where(l => (l.Hidden == False)).SelectMany(fields => fields.Fields)' is not supported.
Does anybody clue what I am doing wrong or how to get it to work?
Do I need to use 2 queries?
Performance is important.
Thanks in advance.
As the error says Enumerable.SelectMany is not supported by LINQ to SharePoint provider
To retrieve the following data
I need only the lists with the properties hidden == false and that got
fields with the description "Special Field".
you could utilize the following query via ClientContext.LoadQuery method:
var lists = ctx.Web.Lists;
var result = ctx.LoadQuery(lists.Where(list => list.Hidden == false).Include(l => l.Fields.Where(f => f.Description == "Special Field")));
ctx.ExecuteQuery();
or
var lists = ctx.Web.Lists;
var result = ctx.LoadQuery(lists.Where(list => list.Hidden == false).Include(l => l.Title,l => l.Fields.Where(f => f.Description == "Special Field").Include( f => f.Title, f => f.Description)));
ctx.ExecuteQuery();
where you could specify what properties needs to be returned like List.Title, Field.Title and Field.Description is this case
Update
To return only lists which contains a specific field, the following filter could be applied:
var foundLists = result.Where(l => l.Fields.Count > 0).ToList();
My XML looks so:
<sensor>
<data>26.7</data>
<data>54.53</data>
<log>false</log>
</sensor>
To retrieve the data fields in a list, I use:
List<string> list = xml.Root.Descendants("sensor").Elements("data").Select(element => element.Value).ToList();
And it works well.
Sometimes the XML looks like this (log = true):
<sensor>
<data>26.7</data>
<data>54.53</data>
<log>true</log>
</sensor>
And I want to ignore these values. How can I achieve this?
I tried this:
var lastUser = xml.Element("response").Descendants("sensor")
.First(u => u.Element("data") != null
&& u.Element("log").Value == "false");
But I only can retrieve of course only the first value.
Why do you use First if you don't want to select the first element? Use Where instead.
var data = xml.Element("response")
.Descendants("sensor")
.Where(x => ((string)x.Element("log")) == "false")
.Elements("data")
.Select(x => x.Value)
.ToList();
Why not use .Where instead of .First and you should be getting an array back?
Use casting nodes to bool or to double:
var data= xml.Element("response").Descendants("sensor")
.Where(s => (bool)s.Element("log"))
.Elements("data")
.Select(d => (double)d)
.ToList(); // produces List<double>
And yes #Daniel is right - First returns only first element from sequence which matches your conditions.
Also consider to use XPath
var data = xdoc.XPathSelectElements("response/sensor[log='true']/data")
.Select(d => (double)d)
.ToList();
I'm trying to get distinct string values out of an Ax repository, but I'm getting a lot of identical strings out (strings only contains numbers)
var ret = context.XInventTransBackOrder
.Where(i => i.BatchRouteId != "")
.Select(i => i.BatchRouteId)
.Distinct()
.ToList();
Where am I going wrong?
Have you tried
var ret = context.XInventTransBackOrder
.Where(i => i.BatchRouteId != "")
.Select(i => i.BatchRouteId)
.ToList();
ret = ret
.Distinct()
.ToList();
If the BatchRouteId was a XElement, for instance, then probably an object reference comparison would be performed. In that case change the code to
var ret = context.XInventTransBackOrder
.Where(i => i.BatchRouteId != null && !String.IsNullOrEmpty(i.BatchRouteId.Value))
.Select(i => i.BatchRouteId.Value)
.Distinct()
.ToList();
UPDATE #1
Note that some types implement implicit conversions making you think they were another type. You can pass a string to a XName parameter without explicit casting, and the string will automatically be converted to XName.
UPDATE #2
According to a comment of nk2003dec the context is LinqToDynamicsAx. I don't know this interface but probably it does not implement Distinct. What you can to in such a case, is to change the context form a XY-LINQ to Object-LINQ by using the System.Linq.Enumerable.AsEnumerable<TSource> extension method
var ret = context.XInventTransBackOrder
.Select(i => i.BatchRouteId)
.Where(id => id != "")
.AsEnumerable()
.Distinct()
.ToList();
I also inverted Select and Where as this simplifies the access to BatchRouteId
X++ does not have a distinct operator. The deferred execution will try to execute on ToList() and will fail because of this.
Assuming I have the following list:
IList<string> list = new List<string>();
list.Add("Mouse");
list.Add("Dinner");
list.Add("House");
list.Add("Out");
list.Add("Phone");
list.Add("Hat");
list.Add("Ounce");
Using LINQ how would I select the words containing "ou" and sort the selection such that the words beginning with "ou" are listed at the start and then the words containing but not starting with "ou" are subsequently listed. The list I'm trying to create would be:
Ounce
Out
House
Mouse
I came up with the following but it is not working:
list.Where(x => x.Contains("ou"))
.OrderBy(x => x.StartsWith("ou"))
.Select(x => x);
You're getting a case-sensitive comparison, and also you need OrderByDescending(). A quick and dirty way to achieve the case-insensitivity is ToLowerInvariant():
var result = list.Where(x => x.ToLowerInvariant().Contains("ou"))
.OrderByDescending(x => x.ToLowerInvariant().StartsWith("ou"))
.Select(x => x);
Live example: http://rextester.com/GUR97180
This previous answer shows the correct way to do a case insensitive comparison (ie, dont use my example above, its bad)
Your first mistake is not comparing strings in a case-insensitive way; "Out" and "Ounce" have capital Os and would not return "true" when you use Contains("ou"). The solution is to use ToLower() when checking letters.
list.Where(x => x.ToLower().Contains("ou"))
.OrderByDescending(x => x.ToLower.StartsWith("ou")) //true is greater than false.
.Select(x => x);
Three problems:
You need to assign the result to something, otherwise it is simply discarded.
You need to use OrderByDescending because true sorts after false if you use OrderBy.
You need to use a case-insensitive compare.
Try this:
var needle = "ou";
var stringComparison = StringComparison.OrdinalIgnoreCase;
var query =
from word in list
let index = word.IndexOf(needle, stringComparison)
where index != -1
orderby index
select word;
This will append an empty space to the beginning of words that start with "OU".
var result = list.Where(x => x.ToLowerInvariant().Contains("ou"))
.OrderBy(x => x.ToLowerInvariant()
.StartsWith("ou") ? " " + x : x.Trim());
list = list.Where(x => x.ToLower().Contains("ou"))
.OrderBy(x => !x.ToLower().StartsWith("ou")).ToList();
Or by using the methods of List (changing it from IList to List):
list.RemoveAll(x => !x.ToLower().Contains("ou"));
list.Sort((s1, s2) => -1 * 1.ToLower().StartsWith("ou")
.CompareTo(s2.ToLower().StartsWith("ou")));
I think this is what you're looking for:
list = list.Where(x => x.IndexOf("ou", StringComparison.OrdinalIgnoreCase) >= 0)
.OrderByDescending(x => x.StartsWith("ou", StringComparison.OrdinalIgnoreCase))
.ThenBy(x => x)
.ToList();
Note that instead of converting the strings ToLower (or upper), I use a StringComparison enum (currently OrdinalIgnoreCase). This ensures that it works consistently as expected in any culture. Choose the right case-insensitive comparison depending on your circumstance.
If you prefer the LINQ query syntax that's:
list = (from x in list
where x.IndexOf("ou", StringComparison.OrdinalIgnoreCase) >= 0
orderby x.StartsWith("ou", StringComparison.OrdinalIgnoreCase) descending, x
select x).ToList();
var bla = "ou";
var list = new List<string>{
"Mouse",
"Dinner",
"House",
"Out",
"Phone",
"Hat",
"Ounce"};
var groupa = list.GroupBy(x =>x.ToLower().Contains(bla));
groupa.First().ToList().OrderByDescending(x => x.ToLower().StartsWith(bla));
You can simply call the list.Sort method by passing in an instance of a custom comparer as follows:
public class MyCustomStringComparer: IComparer<string>
{
public int Compare(Entity x, Entity y)
{
int result = 0;
if (x.ToLower().StartsWith("ou") && y.ToLower().StartsWith("ou"))
result = x.Compare(y);
else if (x.ToLower().StartsWith("ou") && !y.ToLower().StartsWith("ou"))
result = -1;
else if (!x.ToLower().StartsWith("ou") && y.ToLower().StartsWith("ou"))
result = 1;
else
result = x.Compare(y);
return (result);
}
}
I was wondering if there is a way to get a list of results into a list with linq to xml. If I would have the following xml for example:
<?xml version="1.0"?>
<Sports xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SportPages>
<SportPage type="test">
<LinkPage>
<IDList>
<string>1</string>
<string>2</string>
</IDList>
</LinkPage>
</SportPage>
</SportPages>
</Sports>
How could I get a list of strings from the IDList?
I'm fairly new to linq to xml so I just tried some stuff out, I'm currently at this point:
var IDs = from sportpage in xDoc.Descendants("SportPages").Descendants("SportPage")
where sportpage.Attribute("type").Value == "Karate"
select new
{
ID = sportpage.Element("LinkPage").Element("IDList").Elements("string")
};
But the var is to chaotic to read decently. Isn't there a way I could just get a list of strings from this?
Thanks
This query works - tested and verified:
var ID2 = (from sportpage in xDoc.Descendants("SportPages").Descendants("SportPage")
where sportpage.Attribute("type").Value == "Karate"
select sportpage)
.Descendants("LinkPage")
.Descendants("IDList")
.Elements("string")
.Select(d => d.Value)
.ToList();
Gives me a list of two strings, "1" and "2".
var myStrings = xDoc.Descendants("SportPage")
.Where(d => d.Attribute("type").Value == "Karate")
.Descendants("IDList")
.Descendants("string")
.Select(d => d.Value);
to see your string:
xDoc.Descendants("SportPage")
.Descendants("IDList")
.Where(d => d.Attribute("type").Value == "Karate")
.Descendants("string")
.Select(d => d.Value)
.ToList()
.ForEach(Console.WriteLine);
Do you mean this?
List<string> IDs = xDoc.Descendants("SportPages").Descendants("SportPage")
.Where( anySportPage => anySportpage.Attribute("type").Value == "Karate" )
.Select( karateSportPage => karateSportpage.Element("LinkPage").Element("IDList").Elements("string"))
.ToList();
I think the reason you find the "var" chaotic is your creation of the anonymous type with the "new" in your select. If you just select the one item you're after then the var will not be an anonymous type.
e.g.
select sportpage.Element("LinkPage").Element("IDList").Elements("string");
However, my preference would be to do that using the . notation like this.
List<string> ids = xDoc.Elements("SportPages").Elements("SportPage").Where(sportPage => sportPage.Attribute("type").Value == "Karate").Elements("LinkPage").Elements("IDList").Elements("string").Select(id => id.Value).ToList();
The biggest issue you were having was that you didn't grab the .Value from the returned element set. But here's another way to do it.
var ids = from sportPage in xDoc.Descendants("SportPage")
let attrib = sportPage.Attribute("type")
where attrib != null
let type = attrib.Value
where !string.IsNullOrEmpty(type)
&& type == "Karate"
from id in sportPage.Descendants("IDList").Elements()
select id.Value;