Finding matches that contain certain text in properties using LINQ - c#

I am trying to convert the following code to LINQ
foreach (var item in pageHistorycol)
{
if (item.PageTitle.Contains(text) || item.PageURI.Contains(text))
{
tempHistory.Insert(0, item);
}
}
The following returns all items instead of the matches
var matches = pageHistorycol.Where(item =>
(item.PageTitle.Contains(text) || item.PageURI.Contains(text)));
What am I missing?

The two statements you displayed should return the same items. If the second returns all items, the foreach loop should add all of the items to tempHistory as well.
The main difference will be your foreach loop will return the items in the opposite order as your LINQ query. If the order of matches is important, you can get the items in the same order using:
var matches = pageHistorycol
.Where(item => item.PageTitle.Contains(text) || item.PageURI.Contains(text))
.Reverse();

Related

Store single result as List<> from loop

Please check Part1 carefully. The variable "singleItem" containing the item during loop of each Ids. But my goal is convert this "singleItem" variable as a List of items. So i can use this "singleItem" in another method like Part2
Part1:
foreach (int Id in Ids)
{
var singleItem = ctx.SingleScannedItems.FirstOrDefault(x => x.SingleScannedItemId == Id);
}
Part2:
public string MyMethod(List<singleItem> items)
{
//do something with items
}
You can use Contains method to query all the items which has SingleScannedItemId property value matching to the values in the Ids collection.
var filteredItems = ctx.SingleScannedItems
.Where(x => Ids.Contains(x.SingleScannedItemId)).ToList();
You do not need the loop now as you are querying all those items you wanted once.
The variable filteredItems will be a list of SingleScannedItem. You can pass that to a method which accepts the collection.
You dont need loop , checking against a Ids list the following would work.
List<singleItem> items = ctx.SingleScannedItems
.Where(x => Ids.Contains(x.SingleScannedItemId).ToList();
MyMethod(items);

How can I merge the results of a group by Linq-to-XML query?

I am trying to construct a Linq-to-XML query that performs the following steps:
groups all descendant nodes in an XDocument
aggregates the distinct items in each group
replaces the elements in the parent of the last item in each group with the aggregated items, in an order of my choosing
deletes all the original items in each group
So far I have the first two steps working with the following code. Note that the MyGroupByKeyFunction is written in a way that guarantees (among other things) that all elements in each group will have the same depth (this is why the orderby works).
var groups =
from e in doc.Root.Descendants()
group e by MyGroupByKeyFunction(e) into g
orderby g.First().Ancestors().Count() descending
select new {
agg = g.Aggregate(new List<XElement>(), (list, el) => {
list.Add(el);
return list;
}).Distinct(new MyCustomXElementEqualityComparer()),
items = g,
target = g.Last().Parent
};
The last two steps are where I am getting stuck. I tried the following but it is not working quite the way I want.
foreach (var group in groups)
{
group.items.Remove();
foreach (var item in group.merge)
{
group.target.Add(item);
}
}
The elements in group.items are successfully removed and the target populated, but I also want the parent elements for the elements in group.items to be removed if the call to group.items.Remove() causes the parent to be emptied. So, I tried replacing that line with the following:
foreach (var delete in group.items)
{
if (delete.Parent.Elements().Count() == 1)
delete.Parent.Remove();
else
delete.Remove();
}
The problem with this is that successive iterations of this loop result can result in a NullReferenceException because the parent element may exist as an item in another group from the original query results! This of course causes delete.Parent to be null because it has previously been detached from the XML tree.
How can I get around this problem?
Update
Per Falanor's suggestion, I have tried modifying the code to the following. However, this causes the final result of the XDocument to only contain the root element. I can't figure out why that is happening. Any thoughts or a better solution to this problem?
HashSet<XElement> removed = new HashSet<XElement>();
foreach (var group in groups)
{
removed.UnionWith(group.items.Select(el => el.Parent).Where(el => !el.Parent.Equals(group.target)));
group.items.Remove();
foreach (var item in group.merge)
{
if (!removed.Contains(item))
group.target.Add(item);
}
}
removed.Where(el => el.Parent != null).Remove();
It turns out Falanor's ideas was right, I just had a small error in the way I wrote the solution that caused it not to work. The method call to UnionWith should have been:
removed.UnionWith(group.items.Select(el => el.Parent).Where(el => !el.Equals(group.target)));
Note the error was in the where clause.
Also, for anyone interested, I realized I could significantly reduce the execution time of my code by adding the following 'where' clause to my initial query (right before the final 'select' statement):
where g.Select(p => p.Parent).Distinct().Count() > 1
This causes the query to only return groupings of elements that belong to different parents. Just to put things in perspective, the XML file I was targeting my code with returned more than 200,000 groupings. With the additional 'where' clause, the number of groupings fell to about 150! And the final result is the same.
Maybe remove the parents(and the children as well) doing this?
foreach (var group in groups)
{
if(group.Parent.Elements().Count() == 1)
group.Parent.Remove();
else
group.items.Remove();
foreach (var item in group.merge)
{
group.target.Add(item);
}
}

Linq remove all

Im trying to replace items in a list with the RemoveAll function, but didnt realize that it completely removes the index of the list. Is there a function that can replace objects instead of remove them? I am comparing strings.
Items.RemoveAll(x => commonItems.Any(y => y.ItemName == x.ItemName));
Or maybe once its removed I replace it with another empty index at that same spot?
You could replace RemoveAll with Where to get the records you want to modify first:
var matches = Items.Where(x => commonItems.Any(y => y.ItemName == x.ItemName));
Then just iterate through the results and replace the value:
foreach (var match in matches)
match.ItemName = "N/A";

Remove an item from a list

why cant I select remove or delete? I want to remove a record from a list
IEnumerable<StockLocation_Table> AllCurrentStocklocations = db.StockLocation_Table.ToList();
List<StockLocation> StockLocations = ServerHelper.GetStockLocationsBatch(BatchUrl, i, batchSize);
foreach (StockLocation_Table _stock_table in AllCurrentStocklocations)
{
foreach (StockLocation _stock in StockLocations)
{
if (_stock.ServerLocationId == _stock_table.ServerLocationId)
{
AllCurrentStocklocations.?? why cant i say remove._stock_table
}
}
}
Because it is IEnumerable<T> and the Remove method is not defined in IEnumerable<T>.Since you are using ToList just use a List<T> as type:
List<StockLocation_Table> AllCurrentStocklocations = db.StockLocation_Table.ToList();
Edit: Also you can't modify the collection inside of foreach loop.You can use LINQ instead:
var AllCurrentStocklocations = db.StockLocation_Table
.Where(x => !StockLocations
.Any(s => s.ServerLocationId == x.ServerLocationId).ToList()
What you want to do here is get all of the items from your DB table where the ID is not in this other list. What you should do here is construct a query such that you get just those items without those IDs, rather than pulling down the entire DB table into a list, and then going through this other list for each item) to look for IDs so that you can remove the current item from this list. In addition to being super inefficient, this would also mean removing the item from a collection being iterated, which would break the iterator. Instead write something that can be translated into a DB query:
List<StockLocation> stockLocations = ServerHelper.GetStockLocationsBatch(
BatchUrl, i, batchSize);
var locationIDs = stockLocations.Select(location => location.ServerLocationId);
db.StockLocation_Table.Where(item =>
!locationIDs.Contains(item.ServerLocationId));

Conditional LINQ query in foreach

I have a multiselection box and I would like to iterate over the selected items and do a linq query, but I'm not sure how to write it. This is what I have so far:
if (lbStateLegislation.Items.Count > 0)
{
foreach (ListItem li in lbStateLegislation.Items)
attributes = vdc.attributes.Where(a => a.fieldvalue == li.Value).ToList();
}
I basically need to construct an OR query, so it is selecting from the collection where there are values for each of the selected items. I think, as it's written now, it is doing an AND query.
Just use the Contains extension method. Linq2Sql will translate it as an IN clause:
var inValues = lbStateLegislation.Items.Select(s => s.Value);
vdc.attributes.Where(a => inValues.Contains(a.fieldvalue));
You may be able to combine the two statements into one, but I will leave that for you to try as I'm not positive that it will work as a single statement.
HTH
var attributes=
vdc.attributes.Where(q=>
lbStateLegislation.Items.Any(o=> o.Value == q.fieldValue))
.Select(o=> o);

Categories