Get Attribute Value by LINQ - linq

The HTML Source is as follows
<img id="itemImage" src="https://www.xyz.com/item1.jpg">
I am using the following LINQ query to get the SRC value (Image Link)
string imageURL = document.DocumentNode.Descendants("img")
.Where(node => node.Attributes["id"] != null && node.Attributes["id"].Value == "itemImage")
.Select(node => node.Attributes["src"].Value).ToString();
But the imageURL gives output as
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[HtmlAgilityPack.HtmlNode,System.String]

The problem is casting it to string. Select() returns IEnumerable<T> so you are basically converting an enumerator to a string (as the error message says). Call First() or Single() or Take(1) in order to get a single element before casting it to a string.
.Select(node => node.Attributes["src"].Value).First().ToString();
Also, if there is a chance that the desired element is not present, FirstOrDefault() and SingleOrDefault() returns null rather then throwing an exception. In that case, I would recommend
var imageUlr = ... .Select(node => node.Attributes["src"].Value).FirstOrDefault();
if (imageUrl != null)
{
// cast it to string and do something with it
}

Add .DefaultIfEmpty(string.Empty)
.FirstOrDefault
string imageURL = document.DocumentNode.Descendants("img")
.Where(node => node.Attributes["id"] != null && node.Attributes["id"].Value == "itemImage")
.Select(node => node.Attributes["src"].Value)
.DefaultIfEmpty(string.Empty)
.FirstOrDefault()
.ToString();

Try adding FirstOrDefault():
string imageURL = document.DocumentNode.Descendants("img")
.Where(node => node.Attributes["id"] != null && node.Attributes["id"].Value == "itemImage")
.Select(node => node.Attributes["src"].Value)
.FirstOrDefault();

Related

System.InvalidOperationException: 'Sequence contains no matching element'

May I know why that error keeps pointing to "String isEmailVerified ...."?
public JsonResult GetMemberCounts([FromBody] ChartFilterRequest filter)
{
DateTime startDate = DateTime.Parse(filter.MainFilter.First(m => m.Name == "startDate").Value as string);
DateTime endDate = DateTime.Parse(filter.MainFilter.First(m => m.Name == "endDate").Value as string);
String isEmailVerified = filter.MainFilter.First(m => m.Name == "isEmailVerified").Value as string;
var data = _dashboardComponent.GetMemberCount(startDate, endDate, isEmailVerified);
return new JsonResult(data);
}
Try using FirstOrDefault and LastOrDefault instead of First and Last, these methods will return the default value of the type they are invoked for if no elements match the lambda expression you provide as a parameter.
In your project, You just use filter.MainFilter.First(xxxx) to select the data, So if no elements match the lambda expression you provide as a parameter,First() will throw an exception, So here will report this error.

Retrieve single element from LINQ query

Working with LINQ for the first time in a while and trying to clean something up. I have the following statements:
var element = await _Entities.References
.Where(db => db.LoadId == request.LoadId && db.ReferenceCode == "123")
.OrderByDescending(rec => rec.Created).FirstOrDefaultAsync(cancellationToken);
if (element != null) {
dto.ElementValue = element.Value;
}
I'd like to condense this into a single statement if possible but I was having trouble getting just the value from the await method.
You could do something like this:
dto.ElementValue = (await _Entity.References
.Where(db => db.LoadId == request.LoadId && db.ReferenceCode == "123")
.OrderByDescending(rec => rec.Created)
.FirstOrDefaultAsync(cancellationToken))?.Value
?? dto.ElementValue;
Note that technically this changes the behaviour of the code. Previously, if the query doesn't return a result, the ElementValue property is not touched. With a one-liner, if the query doesn't return a result, the ElementValue getter and setter will both be called.
Also, if the query returns a result whose Value is null, the ElementValue property will be set to itself rather than null.

FirstOrDefault throws ArgumentnullException

My goal is to extract a specific record that has a parameter value specified by me. The data is taken from an external API.
My query looks like this:
var productId = productsResponse.Where(x => x.Parameters.Any(y => y.Values.Any(z => z.Equals(auctionTitle)))).FirstOrDefault();
And it works fine until where filters out all the records. Then the method aborts and debugging cannot continue.
The problem is:
System.ArgumentNullException: Value cannot be null
because source transferred to FirstOrDefault is null.
I also tried:
var productId = productsResponse.Where(x => x.Parameters.Any(y => y.Values.Any(z => z.Equals(auctionTitle)))).DefaultIfEmpty().First();
Please let me know what topic I should read because I have run out of ideas. I really care to understand where I am wrong.
This can be not an answer but try this construction:
var productId = productsResponse
.Where(x => x.Parameters.SelectMany(y => y.Values)
.Any(z => z == auctionTitle))
.FirstOrDefault();
Also if data came from external API, it may be needed more null check.
var productId = productsResponse
.Where(x => x.Parameters?.SelectMany(y => y.Values ?? Enumerable.Empty<Value>())
?.Any(z => z == auctionTitle) == true)
.FirstOrDefault();

Unable to cast object of type WhereSelectListIterator 2 System.Collections.Generic.List

I am working on these lists to get an item that matches the selected item from the combobox.
private void InitializaMessageElement()
{
if (_selectedTransactionWsName != null)
{
get a transaction webservice name matching the selected item from the drop down here the output=TestWS which is correct
var getTranTypeWsName = TransactionTypeVModel
.GetAllTransactionTypes()
.FirstOrDefault(transTypes =>
transTypes.WsMethodName == _selectedTransactionWsName);
Loop the list of wsnames from the treenode list. Here it gives me all the node I have which is correct.
var wsNameList = MessageElementVModel
.GetAllTreeNodes().Select(ame =>
ame.Children).ToList();//. == getTranTypeWsName.WsMethodName);
find the getTranTypeWsName.WsMethodName in the wsNameList. Here is where I have the problem:
var msgElementList = wsNameList.Select(x => x.Where(ame => getTranTypeWsName != null && ame.Name == getTranTypeWsName.WsMethodName)).ToList();
my MsgElement list:
MsgElementObsList = new ObservableCollection<MessageElementViewModel>(msgElementList);
this.messageElements = _msgElementList;
NotifyPropertyChanged("MessageElements");
}
Here it is throwing the cast error. why is it not working? I am new to LINQ. thanks
As the error is trying to tell you, LINQ methods return special iterator types the implement IEnumerable<T>; they do not return List<T>.
This enables deferred execution.
Since the object isn't actually a List<T>, you can't cast it to a type that it isn't.
If you need a List<T>, you can either call ToList(), or skip LINQ entirely and use List<T>.ConvertAll(), which is like Select(), but does return List<T>.
Modify
MsgElementObsList = new ObservableCollection<MessageElementViewModel>((List<MessageElementViewModel>) msgElementList);
to
MsgElementObsList = new ObservableCollection<MessageElementViewModel>(msgElementList);
This is because although all lists are enumerable, all enumerables are not lists, and this one happens not to be one.
Also, your bool error has to do with returning true in the select. Here's the fixed code for that:
var msgElementList = wsNameList.Select(x =>
x.Where(ame => ame.Name == getTranTypeWsName.WsMethodName));

Orderby and Linq

How can i add orderby to this:
return (from m in response["holder"].Children()
orderby m["name"]
select new SelectListItem
{
Text = m["name"].ToString(),
Value = m["name"].ToString()
}).ToList();
The problem is that the json returned in the response variable has a list of names, all of them have an uppercase first letter apart from one so they all get ordered fine except the one with the lower case which gets stuck at the bottom of the SelectListItem list.
Any ideas?
Thanks in advance..
EDIT:
Additional info - i am using JSON.NET to parse the json response. And the response variable is a JObject.
You'll need to normalize the data during your orderby. In my example I've chosen to use the ToUpperInvariant() method:
return (from m in response["holder"].Children()
orderby m["name"].ToUpperInvariant()
select new SelectListItem
{
Text = m["name"].ToString(),
Value = m["name"].ToString()
}).ToList();
I'm also assuming that m["name"] is already a String object. If it's not, change the line to:
orderby m["name"].ToString().ToUpperInvariant()
Maybe something like this:
return (from m in response["holder"].Children()
orderby m["name"].ToString().ToLower()
select new SelectListItem
{
Text = m["name"].ToString(),
Value = m["name"].ToString()
}).ToList();
When using the OrderBy method in the method syntax, you can specifiy a StringComparer. Passing a StringComparer that ignores casing solves your issue:
response["holder"]
.Children()
.OrderBy(m => m["name"], StringComparer.CurrentCultureIgnoreCase);
response["holder"]
.Children()
.OrderBy(m => m["name"] as String, StringComparer.CurrentCultureIgnoreCase)
.Select(m => new SelectedListItem
{
Text = m["name"].ToString(),
Value = m["name"].ToString())
})
.ToList();
return response["holder"].Children()
.Select(m => m.FirstCharToUpper())
.OrderBy(m => m["name"].ToString())
.Select(m => new SelectedListItem{
Text = m["name"].ToString(),
Value = m["name"].ToString()
})
.ToList();
static class Utility
{
public static string FirstCharToUpper(this string s)
{
return s.First().ToString().ToUpper() + string.Join("", s.Skip(1));
}
}

Resources