In condition using LINQ - linq

OK, another LINQ question. How do I do an "IN" condition using LINQ. I have an IEnumerable list of myObject and want to do something like myObject.Description in('Help', 'Admin', 'Docs'). How can I accomplish this? Thanks

IN in sql is equivalent is Contains in LINQ
string[] countries = new string[] { "UK", "USA", "Australia" };
var customers =
from c in context.Customers
where countries.Contains(c.Country)
select c;

Use Contains on a collection:
string[] descriptions = { "Help", "Admin", "Docs" };
var query = from foo in list
where descriptions.Contains(foo.Description)
select ...;
(For larger collections, a HashSet<T> might be better.)

Related

LINQ GroupBy on single property

I am just not understanding the LINQ non-query syntax for GroupBy.
I have a collection of objects that I want to group by a single property. In this case Name
{ Id="1", Name="Bob", Age="23" }
{ Id="2", Name="Sally", Age="41" }
{ Id="3", Name="Bob", Age="73" }
{ Id="4", Name="Bob", Age="34" }
I would like to end up with a collection of all the unique names
{ Name="Bob" }
{ Name="Sally" }
Based on some examples I looked at I thought this would be the way to do it
var uniqueNameCollection = Persons.GroupBy(x => x.Name).Select(y => y.Key).ToList();
But I ended up with a collection with one item. So I though maybe I was over complicating things with the projection. I tried this
var uniqueNameCollection = Persons.GroupBy(x => x.Name).ToList();
Same result. I ended up with a single item in the collection. What am I doing wrong here? I am just looking to GroupBy the Name property.
var names = Persons.Select(p => p.Name).Distinct().ToList()
If you just want names
LINQ's GroupBy doesn't work the same way that SQL's GROUP BY does.
GroupBy takes a sequence and a function to find the field to group by as parameters, and return a sequence of IGroupings that each have a Key that is the field value that was grouped by and sequence of elements in that group.
IEnumerable<IGrouping<TSource>> GroupBy<TSource, TKey>(
IEnumerable<TSource> sequence,
Func<TSource, TKey> keySelector)
{ ... }
So if you start with a list like this:
class Person
{
public string Name;
}
var people = new List<Person> {
new Person { Name = "Adam" },
new Person { Name = "Eve" }
}
Grouping by name will look like this
IEnumerable<IGrouping<Person>> groups = people.GroupBy(person => person.Name);
You could then select the key from each group like this:
IEnumerable<string> names = groups.Select(group => group.Key);
names will be distinct because if there were multiple people with the same name, they would have been in the same group and there would only be one group with that name.
For what you need, it would probably be more efficient to just select the names and then use Distinct
var names = people.Select(p => p.Name).Distinct();
var uniqueNameCollection = Persons.GroupBy(x => x.Name).Select(y => y.Key).ToList();
Appears valid to me. .net Fiddle showing proper expected outcome: https://dotnetfiddle.net/2hqOvt
Using your data I ran the following code statement
var uniqueNameCollection = people.GroupBy(x => x.Name).Select(y => y.Key).ToList();
The return results were List
Bob
Sally
With 2 items in the List
run the following statement and your count should be 2.
people.GroupBy(x => x.Name).Select(y => y.Key).ToList().Count();
Works for me, download a nugget MoreLinq
using MoreLinq
var distinctitems = list.DistinctBy( u => u.Name);

Select distinct from property which is List<string>

I have a generic list of assets (List<Asset>) and one of the assets properties is called Tags which is a list of strings
How would I do a Linq query to get a distinct list of tags. I tried
assetList.Select(a => a.Tags).Distinct()
but this returns me an IEnumerable<List<string>> instead of an IEnumerable<string>
You was close. You need to use Enumerable.SelectMany to select all tags and flatten them into one sequence:
assetList.SelectMany(a => a.Tags).Distinct()
assetList.SelecMany(a => a.Tags).Distinct() . correct it
more information about differences Difference Between Select and SelectMany
Select only takes objects as they are and Tags is a list, so it takes lists. If you need items from these lists, you have to flatten them into one list and then proceed with other operations.
assetList.SelectMany(a => a.Tags).Distinct();
A nice example from MSDN on SelectMany
PetOwner[] petOwners =
{ new PetOwner { Name="Higa, Sidney",
Pets = new List<string>{ "Scruffy", "Sam" } },
new PetOwner { Name="Ashkenazi, Ronen",
Pets = new List<string>{ "Walker", "Sugar" } },
new PetOwner { Name="Price, Vernette",
Pets = new List<string>{ "Scratches", "Diesel" } } };
IEnumerable<string> query1 = petOwners.SelectMany(petOwner => petOwner.Pets);
produces the following list
Scruffy, Sam, Walker, Sugar, Scratches, Diesel

Searching a list using an array as the parameter list using LINQ

I currently have some code that looks like this
string[] contains = new string[]{"marge", "homer", "lisa", "bart", "maggie"};
string[] actions = new string[]{"get dye", "mmm beer", "blow saxophone", "have a cow", "bang"};
for (int i = 0; i < someActions.Count; ++i)
{
if (someActions.Contains(contains[i]))
callRoutine(actions[i]);
}
(this is a very trivial example - someActions is a List)
I'm wondering if there is a way in LINQ to do the same as loop? I'm thinking of something along the lines of
int i = position in contains or 0
callRoutine(actions[i]);
The problem is that I don't know how to use an array as the search parameter. Various searches suggest I need to use IEnumerable to do this, but I'm not sure.
Any help would be appreciated here.
Paul
This wouldn't work nicely with your current data setup.
If you are flexible on your data, you could try this:
var namesToActions = new Dictionary<string, string>()
{
{ "marge" , "get dye" },
{ "homer", "mmm beer"},
{ "lisa", "blow saxophone"},
{ "bart", "have a cow"},
{ "maggie", "bang"}
};
someActions.ForEach(a => callRoutine(namesToActions[a]));
Switching to a Dictionary makes it a little easier to perform the type of Linq action you're looking for and provides additional flexibility and quicker lookup times.
I'm not sure what your purpose is, but if you want to convert your for loop into a linq statement, you can do this:
var i = 0;
someActions.ForEach(x =>
{
if (someActions.Contains(contains[i]))
callRoutine(actions[i]);
i++;
});
someActions.Intersect(contains).ForEach(callRoutine);
OR
someActions.Intersect(contains).ForEach(i=>callRoutine(i));

linq: Using methods in select clause

I'm breaking my head with this and decided to share my problem with you
I want to create an anonymous select from several tables, some of them may contain more than one result. i want to concatenate these results into one string
i did something like this:
var resultTable = from item in dc.table
select new
{
id= item.id,
name= CreateString((from name in item.Ref_Items_Names
select name.Name).ToList()),
};
and the CreateString() is:
private string CreateString(List<string> list)
{
StringBuilder stringedData = new StringBuilder();
for (int i = 0; i < list.Count; i++)
{
stringedData.Append(list[i] + ", ");
}
return stringedData.ToString();
}
my intentions were to convert the "name" query to list and then sent it to CreateString() to convert it to one long concatenated string.
I tried using .Aggregate((current,next) => current + "," + next);
but when i try to convert my query to DataTable like below:
public DataTable ToDataTable(Object query)
{
DataTable dt = new DataTable();
IDbCommand cmd = dc.GetCommand(query as IQueryable);
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = (SqlCommand)cmd;
cmd.Connection.Open();
adapter.Fill(dt);
cmd.Connection.Close();
return dt;
}
I'm getting exception that "dc.GetCommand()" can't understand query with Aggregate method
later I tried to even use this simple query:
var resultTable = from itemin dc.table
select new
{
name = CreateString()
};
When CreateString() returns "success", nothing was inserted to "name"
why there is no way of using methods in select clause?
Thank you
Yotam
There is difference between LINQ to objects and LINQ to some-db-provider. Generally speaking, when using IQueryable, you can't use any methods, except the ones your provider understands.
What you can do is to retrieve the data from the database and then do the formatting using LINQ to objects:
var data = from item in dc.table
where /* some condition */
select item;
var result = from item in data.AsEnumerable()
select new
{
name = SomeFunction(item)
}
The AsEnumerable() extension method forces processing using LINQ to objects.
Forgive me if I've miss interpreted your question. It seems that what you are trying to do is abstract your select method for reuse. If this is the case, you may consider projection using a lambda expression. For example:
internal static class MyProjectors
{
internal static Expression<Func<Object1, ReturnObject>> StringDataProjector
{
get
{
return d => new Object1()
{
//assignment here
}
}
}
}
Now you can select your datasets as such:
dc.Table.Select(MyProjectors.StringDataProjector)
As for the concatenation logic, what about selecting to some base class with an IEnumerable<string> property and a read-only property to handle the concatenation of the string?

How to add an in command to a where clause in LINQ?

If I have a where clause as follows:
Where item.field == "value"
How can I change the statement in LINQ to be:
Where item.field in ("value1","value2","value3")
Seems simple, not working.
Thanks in advance
Declare your "in" values collection in a variable first:
var collection = new[] { "value1","value2","value3" };
And then use it in query:
...
where collection.Contains(item.field)
...
Or in lambda syntax(say you have a collection you are searching through):
var lookup = new[] { "value1","value2","value3" };
var result = collection.Where(x=> lookup.Contains(x)).ToArray();

Resources