I am using a list collection with two properties Name and Group.
I need to create a list collection which it will contain only elements from first group, then another list with elements from the second group and finally the third group.
My code:
public class Person
{
public string Name { get; set; }
public string Group { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
var mItems = new List<Person>();
mItems.Add(new Person{ Name="A", Group="1", });
mItems.Add(new Person { Name = "B", Group = "1", });
mItems.Add(new Person { Name = "C", Group = "2", });
mItems.Add(new Person { Name = "D", Group = "2", });
mItems.Add(new Person { Name = "E", Group = "3", });
mItems.Add(new Person { Name = "F", Group = "3", });
mItems.Add(new Person { Name = "G", Group = "1", });
}
I need to create a list with elements which are on the first group, example group "1".( i notice that this is an example, i don't not know the exactly group name in my application, it can be anything)
So my expected result is a list with A,B and G, then a second list with C,D and finally a third list with E,F. I have found code below:
var results = mItems.GroupBy(x => x.Group)
.Select(g => g.OrderBy(x => x.Group).FirstOrDefault());
But i am taking the exactly oposite result. I am taking only the first element of each group.
You need to place your FirstOrDefault() call after your Select() as it's currently inside of the select, which is causing the first of each group to be selected:
var results = mItems.GroupBy(x => x.Group)
.Select(g => g.OrderBy(x => x.Group))
.FirstOrDefault();
Likewise, if you wanted to output every group, you could just remove the FirstOrDefault() call altogether. This would return a collection of ordered collections, which you could access individually via the ElementAt() function:
var results = mItems.GroupBy(x => x.Group)
.Select(g => g.OrderBy(x => x.Group))
.ElementAt(someIndex);
Example
You can see a working example here.
Related
I am trying to modify a LINQ query to select some properties into an array but am struggling to achieve part of it.
toRun.AddRange(entity.Properties
.Select(property => property.PrimaryField)
.Select(field => new { field, entity = entity.EntityName, table = field.Table, key = entity.EntityIdField })
I need this amending so that if a second property called SecondaryField is not null or empty string it will be added to the results of the first Select statement.
For example if entity.Properties contains:
Property { PrimaryField = "a", SecondaryField = "b" },
Property { PrimaryField = "c", SecondaryField = "" }
I would like the first Select statement to return:
{ "a", "b", "c" }
Appreciate any help thanks.
This seems to reproduce what you want: you have a class with two properties:
public class Foo
{
public string Bar { get; set; }
public string Baz { get; set; }
}
Of which you have a collection:
var foos = new List<Foo>
{
new Foo { Bar = "a", Baz = "b" },
new Foo { Bar = "c", Baz = "" },
};
And from this collection, you want to select all properties that have a non-empty value into an array.
You can do so using SelectMany():
var result = foos.SelectMany(f => new[] { f.Bar, f.Baz })
.Where(p => !string.IsNullOrWhiteSpace(p))
.ToArray();
You select a new array containing the value of both properties, then filter out the values you don't want, and turn the result into an array again.
This should be pretty simple - get both fields, use a Where to remove the null/empties and turn to an array:
var result = entity.Properties.SelectMany(p =>new[]{ p.PrimaryField,p.SecondaryField})
.Where(x => !String.IsNullOrEmpty(x))
.ToArray();
Live example: http://rextester.com/MHM61977
Let's say we have 2 sets
A = [ PersonA,PersonB];
B = [ ManagerZ,ManagerT,ManagerY];
Result : ManagerT,ManagerY
There exists One to One mapping between the objects in A and the objects in B.
I'm interested in those objects in B for which exists such an entry in A.
For completeness let's say we are matching on property named Name
Try following
SetB.Where(b => SetA.Any(a => a.Name == b.Name))
You have to perform a join on both lists:
var query =
from person in persons
join manager in managers on person.Name equals manager.Name
select new { Person = person, Manager = manager };
This will select all data from your Person-dataset together with the corresponding data from Manager-dataset.
Alternativly you can flatten the results into a datatype providing the whole data for every match:
select new { Name = person.Name, Age = person.Age, Departement = Manager.Departement }
Or if you´re only interested on the items from B which match use simply select manager.
Try with this code:
List<BType> result = B.Where(x >= A.Exists(y => y.Name == x.Name)).ToList();
In this way you mantain only managers that exists in people list.
Also you can use Intersect.
Example:
public class Person
{
public string Name { get; set; }
}
public class PersonEqualityComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.Name.Equals(x.Name);
}
public int GetHashCode(Person obj)
{
return obj.Name.GetHashCode();
}
}
And now you can use:
var persons = new List<Person>() { new Person { Name = "John" } };
var managers = new List<Person>() { new Person { Name = "John" } };
var results = persons.Intersect(managers, new PersonEqualityComparer());
If you want compare two different class just edit Comparer.
Problem: "The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'."
list2 = list1.Where(a).OrderBy(x => x.Something).Union(list1.Where(x).OrderBy(x => x.SomethingDifferent));
return list2.ToPagedList(...);
I assume this happens because it requires a new 'OrderBy' after the 'Union'. Is there any way to turn this into an OrderedList while keeping the current order from the Union?
Is there a way to add another OrderBy which doesn't actually change the order like:
list2 = list1.Where(a).OrderBy(x => x.Something).Union(list1.Where(x).OrderBy(x => x.SomethingDifferent)).OrderBy(x => x.NothingThatWillAffectTheOrder);
EDIT
To make this more clear, I will explain using a practical example.
Say a user searches in a movie database and you want to show Titles that contain the search string before say Actors' names that contain the search string.
list = movies.Where(x => x.Title.Contain...).Union(movies.Where(x => x.Actors.Contain..)
PagedList will not accept this because it is not ordered but ordering this list will defeat the purpose of the Union. Is there a 'work around' to make this an ordered list while keeping the current order?
I'm not sure why you are getting the error that you are. However, I was able to write a program based on the information that you provided that works properly. Have a look at the following. Hopefully it will help you spot exactly what you are doing differently. Note that my implementation of ToString() uses a feature new in C# 6. If you're on an older version of C#, you'll have to slightly modify that line.
using PagedList;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Actor
{
public string Title { get; set; }
public List<string> Actors { get; set; }
public override string ToString()
{
return $"{Title} featuring {string.Join(", ", Actors)}";
}
}
class Program
{
static void Main(string[] args)
{
List<Actor> movies = new List<Actor>()
{
new Actor() { Title = "Star Wars", Actors = new List<string>() { "Mark Hamill", "Harrison Ford" } },
new Actor() { Title = "Indiana Jones and The Raiders of the Lost Ark", Actors = new List<string>() { "Karen Allen", "Harrison Ford" } },
new Actor() { Title = "Indiana Jones and The Temple of Doom", Actors = new List<string>() { "Kate Kapshaw", "Harrison Ford" } },
new Actor() { Title = "Indiana Jones and The Kingdom of the Crystal Skull", Actors = new List<string>() { "Cate Blanchett", "Harrison Ford" } },
new Actor() { Title = "Jessica Jones", Actors = new List<string>() { "Krysten Riter", "David Tennant" } },
new Actor() { Title = "The Wizard of Oz", Actors = new List<string>() { "Judy Garland" } },
};
var list = movies.Where(x => x.Title.Contains("Jones")).Union(movies.Where(x => x.Actors.Contains("Harrison")));
var pagedList = list.ToPagedList(1, 2);
foreach (var movie in pagedList)
{
Console.WriteLine(movie);
}
}
}
}
There is a select overload that lets you include the ordinal, so perhaps you could do something like:
list1.Where(a).OrderBy(x => x.Something)
.Union(list1.Where(x).OrderBy(x => x.SomethingDifferent))
.Select((item,idx) => new { item, idx })
.OrderBy(x => x.idx);
Unfortunately that means you are not selecting the same type as before. But depending on what you are actually doing with it, maybe you can just flatten it with something like:
.Select((item,idx) => new { item.Title, item.SomeOtherProperty, idx })
Which if whatever is consuming it can duck type, might be viable
I have IEnumerable collection like following
IEnumerable<Customer> items = new Customer[]
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
I want to get value using key Id
I tried like following
public int GetValue(IEnumerable<T> items,string propertyName)
{
for (int i = 0; i < items.Count(); i++)
{
(typeof(T).GetType().GetProperty(propertyName).GetValue(typeof(T), null));
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
}
}
If you want to retrieve a Customer name from a collection by its Id:
public string GetCustomerName(IEnumerable<Customer> customers, int id)
{
return customers.First(c => c.Id == id).Name;
}
Using LINQ you can get all customers names (values) having specific value in this way:
var valuesList = items.Where(x => x.Something == myVar).Select(v => v.Name).ToList();
For single customer name you can do this:
var singleName = items.FirstOrDefault(x => x.Id == 1)?.Name;
Obviously, the Id can be 1, 2 or any other.
Edit:
I recommend you List<Customer> instead of Customer[]
So,
var items = new List<Customer>
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
If I understand you correctly
public static IEnumerable<object> GetValues<T>(IEnumerable<T> items, string propertyName)
{
Type type = typeof(T);
var prop = type.GetProperty(propertyName);
foreach (var item in items)
yield return prop.GetValue(item, null);
}
Just use LINQ to achieve what you want to do. if you want to retrieve a specific value you can use where like this:
public Customer GetCustomerById(IEnumerable<Customer> items,int key)
{
return items.Where(x=>x.id==key)
.Select(x =>x.Name)
.First();
}
this will retrieve the customer who match a specific Id.
Do you want to look things up repeatedly after creating the list? If so, you might want to consider creating a dictionary to do the lookups, like so:
IEnumerable<Customer> items = new Customer[]
{
new Customer {Name = "test1", Id = 999},
new Customer {Name = "test2", Id = 989}
};
var lookup = items.ToDictionary(itemKeySelector => itemKeySelector.Id);
var result = lookup[989];
Console.WriteLine(result.Name); // Prints "test2".
I'm assuming that you don't create the collection in the first place - if you had control over creating the original collection you could use a dictionary in the first place.
private TextBox [] Collectionstextboxonpanel(Panel panel)
{
var textBoxspanel1 = panel.Controls.OfType<TextBox>(); // select controls on panle1 by type
IEnumerable<TextBox> textBoxes = textBoxspanel1; // create collection if need
TextBox[] textBoxes1 = textBoxes.ToArray(); // Array collection
return textBoxes1; // get back TextBox Collection
}
I want know to make a query using linq, between a collection of objects
and a collection of values. In the sample code below, I make the question about it.
class USER
{
public string Name { get; set; }
}
public class MyClass
{
public MyClass()
{
List<USER> listUser = new List<USER>();
listUser.Add(new USER { Name = "A" });
listUser.Add(new USER { Name = "B" });
listUser.Add(new USER { Name = "C" });
listUser.Add(new USER { Name = "D" });
string[] arrayNames = { "A", "B" };
}
}
Using Linq how can I get all USER in listUser with them Name equals to the arrayNames values.?
The expected results wold be
//listUser[0] --> User with Name == "A"
//listUser[1] --> User with Name == "B"
Thanks in advance.
HashSet<string> names = new HashSet<string>(new string[]{ "A", "B" });
var selectedUsers = listUser.Where(user => names.Contains(user.Name));
The hashset is optional and overkill if you have only a few users but it guarantees optimal lookup performance if you have a lot of users.
listUser.Where(u => arrayNames.Contains(u.Name)).ToList();
This should so what you want, tried it in a console app. Worked a treat
var query = listUser.Where(i => arrayNames.Contains(i.Name));
foreach (var item in query)
{
Console.WriteLine(item.Name);
}
Console.ReadKey();
The key part is the arrayNames.Contains(i.Name) as this is inverse to intuition in that you specify the IEnumerable and then the linq parameter inside the contains method.
listUser = (from u in listUser where arrayNames.Contains(u.Name) select u).ToList();