Location of XElement when querying over IEnumerable using LINQ - c#

I have a linq query that is querying over IEnumberable. When I have a matching element for my where clause I would like to know the position of the element in the IEnumberable.
var result = from e in elements
where (string) e.Attribute("class") == "something"
select e.Position();
The e.Position() of course does not compile. The value of e.Position() would be the position of the selected element in the elements IEnumberable.
Any ideas on how to do this?

You need to use the overloaded Select method that allows for an index since that capability is not available in query syntax.
elements.Select((e, i) => new { Element = e, Index = i })
.Where(item => (string)item.Element.Attribute("class") == "something")
.Select(item => item.Index);

If you're using .NET 4.0 then you can use the (new) Zip method and write the same thing using the query syntax as well. It creates some temporary objects, so it isn't as efficient, but some people may find it more readable:
var result = from e in elements.Zip
(Enumerable.Range(0, elements.Count()), Tuple.Create)
where (string)e.Item1.Attribute("class") == "something"
select e.Item2;
It 'zips' the input collection with a generated sequence of numbers (with the same range as is the length of the collection). Then you can store the combined value either using the Tuple class (that's what I did) or you could use anonymous type.

Related

Retrieving non-duplicates from 2 Collections using LINQ

Background: I have two Collections of different types of objects with different name properties (both strings). Objects in Collection1 have a field called Name, objects in Collection2 have a field called Field.
I needed to compare these 2 properties, and get items from Collection1 where there is not a match in Collection2 based on that string property (Collection1 will always have a greater or equal number of items. All items should have a matching item by Name/Field in Collection2 when finished).
The question: I've found answers using Lists and they have helped me a little(for what it's worth, I'm using Collections). I did find this answer which appears to be working for me, however I would like to convert what I've done from query syntax (if that's what it's called?) to a LINQ query. See below:
//Query for results. This code is what I'm specifically trying to convert.
var result = (from item in Collection1
where !Collection2.Any(x => x.ColumnName == item.FieldName)
select item).ToList();
//** Remove items in result from Collection1**
//...
I'm really not at all familiar with either syntax (working on it), but I think I generally understand what this is doing. I'm struggling trying to convert this to LINQ syntax though and I'd like to learn both of these options rather than some sort of nested loop.
End goal after I remove the query results from Collection1: Collection1.Count == Collection2 and the following is true for each item in the collection: ItemFromCollection1.Name == SomeItemFromCollection2.Field (if that makes sense...)
You can convert this to LINQ methods like this:
var result = Collection1.Where(item => !Collection2.Any(x => x.ColumnName == item.FieldName))
.ToList();
Your first query is the opposite of what you asked for. It's finding records that don't have an equivalent. The following will return all records in Collection1 where there is an equivalent:
var results=Collection1.Where(c1=>!Collection2.Any(c2=>c2.Field==c1.Name));
Please note that this isn't the fastest approach, especially if there is a large number of records in collection2. You can find ways of speeding it up through HashSets or Lookups.
if you want to get a list of non duplicate values to be retained then do the following.
List<string> listNonDup = new List<String>{"6","1","2","4","6","5","1"};
var singles = listNonDup.GroupBy(n => n)
.Where(g => g.Count() == 1)
.Select(g => g.Key).ToList();
Yields: 2, 4, 5
if you want a list of all the duplicate values then you can do the opposite
var duplicatesxx = listNonDup.GroupBy(s => s)
.SelectMany(g => g.Skip(1)).ToList();

EF Intersect Syntax

A UI allows users to select one or many tags. I would like to select all Nodes that have ALL that tags the user entered associated, not just a single tag.
public JsonResult SearchNodesByTags(string[] tags)
{
var dbTags = _DbContext.Tags.Where(t => tags.Contains(t.DisplayName)).ToList();
var nodes = _DbContext.Nodes.Where(n => n.Tags.Intersect(dbTags).Any());
// Error about intersection with non primitive
return Json(nodes);
}
You can do this in one statement:
var nodes = _DbContext.Nodes
.Where(n => n.Tags.All(t => tags.Contains(t.DisplayName)));
Your statement is not correct because dbTags is a local list containing Tag objects. When you use this list in a LINQ-to-Entities expression there is no way to translate these objects into SQL variables. That's only possible with primitive values.
var nodes = _DbContext.Nodes.Where(n => n.Tags.Intersect(dbTags).Any());
First of all, you can't use objects in a Linq-to-Entities expression, so you'd have to use something like this to compare:
n.Tags.Select(t => t.DisplayName).Intersect(tags)
Second, Intersect will give you the set of items that are in both given sets, so you'll end up with all Nodes that has any of the tags, instead of all nodes that have all of the tags.
If you want all the Nodes that contain every Tag in tags, you might want to use the answer from here on subsets:
_DbContext.Nodes
.Where(n => !tags.Except(n.Tags.Select(t => t.DisplayName)).Any())
.Select(...
set1.Except(set2) contains elements of set1 that aren't in set2
!set1.Except(set2).Any() == true if set2 includes every element of set1

nested foreach as LINQ with chained linked List

I have a List within a list. I only need the one value from the list and can do obtain my result with a nested foreach but I want to use a LINQ query of some sort.
my code:
var myCity = from c in CountryLists
select (from city in c.stateList
where city.name == passedInValue
select city.name).FirstorDefault();
This returns myCity as a list of some sort with all values as null EXCEPT for where the match was found.
i don't want to have to walk through the city list to find the name. How can I have only one value in myCity; either null or the desired name?
First, use SelectMany to flatten the list, then FirstOrDefault to filter:
CountryList.SelectMany(c => c.stateList).FirstOrDefault(d => d.Name == passedInValue);
Note that because FirstOrDefault can take a predicate, you don't actually need the Where clause.
How about using SelectMany:
var city = CountryLists
.SelectMany(x => x.stateList)
.FirstOrDefault(x => x.name == passedInValue);
You can use SelectMany as other have pointed out (and I prefer that solution myself), however if you'd like the query syntax, you can use multiple from clauses (check the MSDN documentation for more examples):
var city = (from c in CountryLists
from city in c.stateList
where city.name == passedInValue
select city.name).FirstOrDefault();
It is equivalent to the SelectMany method solution, it uses it under the covers anyway.

Using Linq lambdas, how can I get the first item in a two-key-sorted list?

I know this is simple, but my mind is playing tricks on me right now. If we have a flat list of objects with the properties GroupSortIndex and ItemSortIndex (within the group) and we want to find the first item in the list, what's the Linq/lambda for that?
About all I can think of is (meta, not literal code...)
var soughtItem = Source.OrderBy(ItemSortIndex).OrderBy(GroupSortIndex).ToList()[0]
...but that just looks so wrong to me for some reason.
Read post : Default Extension methods to get difference between first and firstordefault
you can use FirstOrDefualt() or First() function
var soughtItem = Source.OrderBy(ItemSortIndex).
ThenBy(GroupSortIndex).FirstOrDefualt();
if(soughtItem !=null)//advantage of using firstordefault
{
}
its better to use FirstOrDefualt because if there is no data it will return null intead of excetipn
You can use IOrderedEnumerable.ThenBy (Note: an IOrderedEnumerable is returned from IEnumerable.OrderBy):
var firstItem = source.OrderBy(s => s.GroupSortIndex)
.ThenBy(s => s.ItemSortIndex)
.First();
This orders first by the group and then by the item. You should use FirstOrDefault if the sequence can be empty. Otherwise First raises an exception.
(i've assumed that you want to order first by group and then by the item instead, since the ItemSortIndex is the index of the item within the group(as mentioned))
var soughtItem = Source
.OrderBy(ItemSortIndex)
.ThenBy(GroupSortIndex).First();
If ItemSortIndex and GroupSortIndex are properties instead of functions, then you need:
var soughtItem = Source
.OrderBy(i => ItemSortIndex)
.ThenBy(i => GroupSortIndex).First();

Search within a list in C# [duplicate]

This question already has answers here:
How can I find a specific element in a List<T>?
(8 answers)
Closed 6 years ago.
I have a list containing the following structure.
class CompareDesignGroup
{
string FieldId;
string Caption;
}
The list is containing items of the above structure.
Is it possible to retrieve an element of the list if FieldId is known?
You can use the Find method on the generic list class. The find method takes a predicate that lets you filter/search the list for a single item.
List<CompareDesignGroup> list = // ..;
CompareDesignGroup item = list.Find(c => c.FieldId == "SomeFieldId");
item will be null if there is no matching item in the list.
If you need to find more than one item you can use the FindAll method:
List<CompareDesignGroup> list = // ..;
List<CompareDesignGroup> result= list.FindAll(c => c.FieldId == "SomeFieldId");
You can use LINQ like this:
CompareDesignGroup result = yourList.FirstOrDefault(x => x.FieldId == yourKnownId);
If you use the FirstOrDefault method the result will be null when list doesn't contain a record with a known id. So before using result check if it is not null.
There are a plethora of methods to find an item inside a list.
LINQ provides extensions method useful to work with collections that does not provide their own search features (or when you do not have the collection itself but a generic interface like IEnumerable<T>). If you have a List<CompareDesignGroup> object and you'll work on that object you can use the methods provided by that class (specialized methods are almost always faster than LINQ methods, they know collection's internal structure and does not have to rely on many abstraction layers).
In all examples I'll perform a culture invariant and case sensitive comparison for FieldId to a hypothetical id parameter. This may not be what you need and you may have to change according to your requirements.
Using List<T>
Given a list declared as:
List<CompareDesignGroup>() list = new List<CompareDesignGroup>();
To find first element that matches the search criteria (it'll return null if no items have been found):
CompareDesignGroup item = list.Find(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
To find all the elements that matches the search criteria:
List<CompareDesignGroup> items = list.FindAll(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
Using IEnumerable<T> (or IList<T>, for example)
Given a list declared as:
IEnumerable<CompareDesignGroup> list = ...
To find first element that matches the search criteria (null if no items have been found):
CompareDesignGroup item = list.FirstOrDefault(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
To find the first element that matches the search criteria (or throw an exception if no items have been found):
CompareDesignGroup item = list.First(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
To find all elements that matches the search criteria:
IEnumerable<CompareDesignGroup> item = list.Where(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
There are many LINQ extensions methods, I suggest to take a look to them all to find the one that better suits your needs.
You can use Where and then you can use FirstOrDefault. That is an LINQ expression.
var ls = new List<CompareDesignGroup>();
var result = ls.Where(a => a.FieldId=="123").FirstOrDefault();
Or SingleOrDefault to get the item you want. Like this:
var ls = new List<CompareDesignGroup>();
var result = ls.Where(a => a.FieldId=="123").SingleOrDefault()
Or even simpler:
var result = ls.SingleOrDefault(a => a.FieldId=="123");
var result2 = ls.FirstOrDefault(a => a.FieldId=="123");
Yes. Use LINQ or the built-in functionalities of List.
List<CompareDesignGroup> listData = new List<CompareDesignGroup>(); // init the data
var result = listData.Where(x=> String.Equals(x.FieldID,"FIELDID KNOWN VALUE"); // gets all data
var first = listData.FirstOrDefault(x=> String.Equals(x.FieldID,"FIELDID KNOWN VALUE"); // gets first search result

Categories

Resources