We Have 2 dimensional string array like
"0" => {"John","23"},
"1" => {"Doe","12"},
"2" => {"Maria","41"},
.......
We want to sort this array like
"0" => {"Maria","41"},
"1" => {"John","23"},
"2" => {"Doe","12"},
.......
Our Array Code String[,] kelimedeger = new String[20, 2];
We want order by kelimedeger[i,1]
Regarding Sorting
One of the problems I see here is that your second "string" is actually not a string, but rather a number. As such, you actually have a person with a name and an age.
Why does this matter?
Sorting depends on the types of data. Strings are sorted alphabetically, while numbers are sorted numerically.
Consider the following list:
1, 2, 17, 11, 100, 20, 34
This can be sorted in multiple ways
Numerical Alphabetical
--------- ------------
1 1
2 100
11 11
17 17
20 2
34 20
100 34
Given that you will most likely want to sort numerically, you need to store your data as int, not as string.
How to store the data?
This depends on your use-case. If names are guaranteed to be unique, then you could use a Dictionary<string,int>. Otherwise, I advise you to create a class Person and use a ICollection<Person> to store them.
As Dictionary<string, int>
This approach is useful if names are guaranteed to be unique in your domain. Further, it only uses built-in types.
namespace DictionaryTest
{
public class Program
{
public static void Main(string[] args)
{
//Create a dictionary to store people
Dictionary<string, int> people = new Dictionary<string, int>();
//Add some people. Note that this is type-safe
people.Add("John", 23);
people.Add("Doe", 12);
people.Add("Maria", 41);
//people.Add("John", 55); // <-- This will fail because there is already a John
//Create queries to ensure correct sorting
var peopleByName = from p in people
orderby p.Key //Our name is the key, the age is the value
select new {Name = p.Key, Age = p.Value};
var peopleByAge = from p in people
orderby p.Value
select new {Name = p.Key, Age = p.Value};
var peopleByAgeDescending = from p in people
orderby p.Value descending
select new {Name = p.Key, Age = p.Value};
//Execute the query and print results
foreach(var person in peopleByAge)
{
Console.WriteLine("Hello, my name is {0} and I am {1} years old", person.Name, person.Age);
}
}
}
}
Try it online!
As ICollection<Person>
This approach defines a class Person, which only holds a Name and an Age property, but can be extended to contain much more information, methods, etc.
namespace ClassTest
{
public class Program
{
public static void Main(string[] args)
{
//Create a list to store people
ICollection<Person> people = new List<Person>();
//Add some people. Note that this is type-safe
people.Add(new Person(){ Name = "John", Age = 23, FavouriteColour = "Blue" });
people.Add(new Person(){ Name = "Doe", Age = 12});
people.Add(new Person(){ Name = "Maria", Age = 41, FavouriteColour = "Purple" });
people.Add(new Person(){ Name = "John", Age = 55, FavouriteColour = "Gray" }); //<-- You can indeed have two people with the same name
//Create queries to ensure correct sorting
var peopleByName = from p in people
orderby p.Name
select p;
var peopleByAge = from p in people
orderby p.Age
select p;
var peopleByAgeDescending = from p in people
orderby p.Age descending
select p;
//Execute the query and print results
foreach(var person in peopleByAge)
{
Console.WriteLine("Hello, my name is {0} and I am {1} years old.", person.Name, person.Age);
if(person.FavouriteColour != null)
{
Console.WriteLine("My favourite colour is {0}.", person.FavouriteColour);
}
else
{
Console.WriteLine("I have no favourite colour.");
}
Console.WriteLine(); //Add a new line for better readability
}
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string FavouriteColour { get; set; }
}
}
Try it online!
I personally like the second approach better, as it is more extendable and does not have a requirement for uniqueness of the name. It allows you to extend the Person class as much as you like, and gives you many more options for sorting.
To sort a two-dimensional array you need to convert each row to a one-dimensional array and save it in a List or something similar. Then you can sort the list by the column. After sorting you need to convert the list back to a two-dimensional array.
Here is a method you could use for sorting:
public static T[,] Sort2DArray<T>(T[,] array, int column, bool ascending = true)
{
int i = 0;
List<T[]> items = new List<T[]>();
int columns = array.GetLength(1);
int rows = array.GetLength(0);
T[] obj = new T[columns];
foreach (var item in array)
{
obj[i % columns] = item;
if ((i + 1) % 2 == 0)
{
items.Add(obj);
obj = new T[columns];
}
i++;
}
var ordered = ascending ? items.OrderBy(a => a[column]) : items.OrderByDescending(a => a[column]);
T[,] result = new T[rows, columns];
for (int r = 0; r < rows; r++)
{
var row = ordered.ElementAt(r);
for (int c = 0; c < columns; c++)
{
result[r, c] = row[c];
}
}
return result;
}
Your code would look like this:
string[,] array =
{
{"John", "23" },
{"Doe", "12" },
{"Maria", "41" },
};
string[,] ordered = Sort2DArray(array, 1);//Sort by column 1 / Age
Converting a two-dimensional array to one- dimensional array and back is not the best routine to sort your values. The best way is to create a class to store your data, just as David Stockinger and Tim Schmelter said.
Related
I have LINQ query that I want to generate dynamically:
var groupData =
from l in data
group l by l.Field1 into field1Group
select new MenuItem()
{
Key = field1Group.Key,
Count = field1Group.Count(),
Items = (from k in field1Group
group k by k.Field2 into field2Group
select new MenuItem()
{
Key = field2Group.Key,
Count = field2Group.Count()
}).ToList()
};
The ultimate goal is to be able to dynamically group the data by any combination of fields with no limit on the nested queries.
I can get as far as the first level but I'm struggling with the nested sub queries:
string field1 = "Field1";
string field2 = "Field2";
var groupDataD =
data.
GroupBy(field1, "it").
Select("new ( it.Key, it.Count() as Count )");
Is this possible with chained dynamic LINQ? Or is there a better way to achieve this?
The following should work (though personally I would rather avoid using such code):
Follow this answer to add the following in ParseAggregate, :
Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
{
// Change starts here
var originalIt = it;
var originalOuterIt = outerIt;
// Change ends here
outerIt = it;
ParameterExpression innerIt = Expression.Parameter(elementType, elementType.Name);
it = innerIt;
Expression[] args = ParseArgumentList();
// Change starts here
it = originalIt;
outerIt = originalOuterIt;
// Change ends here
...
}
Add Select, GroupBy, ToList into IEnumerableSignatures, and respective conditions in ParseAggregate, as explained in this answer:
interface IEnumerableSignatures
{
...
void GroupBy(object selector);
void Select(object selector);
void ToList();
...
}
Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
{
...
if (signature.Name == "Min" ||
signature.Name == "Max" ||
signature.Name == "GroupBy" ||
signature.Name == "Select")
...
}
Finally, Your query would be:
string field1 = "Field1";
string field2 = "Field2";
var result =
data
.GroupBy(field1, "it")
.Select($#"new (
it.Key,
it.Count() as Count,
it.GroupBy({field2})
.Select(new (it.Key, it.Count() as Count))
.ToList() as Items
)");
Note that "it" holds a different instance when used in the parent query vs. the subquery. I tried to take advantage of "outerIt" to overcome this conflation, but unfortunately without success (but maybe you'd succeed? maybe 1, 2 would help)
A simple example for future reference:
public class Person
{
public string State { get; set; }
public int Age { get; set; }
}
public static Main()
{
var persons = new List<Person>
{
new Person { State = "CA", Age = 20 },
new Person { State = "CA", Age = 20 },
new Person { State = "CA", Age = 30 },
new Person { State = "WA", Age = 60 },
new Person { State = "WA", Age = 70 },
};
var result = persons
.GroupBy("State", "it")
.Select(#"new (
it.Key,
it.Count() as Count,
it.GroupBy(Age)
.Select(new (it.Key, it.Count() as Count))
.ToList() as Items
)");
foreach (dynamic group in result)
{
Console.WriteLine($"Group.Key: {group.Key}");
foreach (dynamic subGroup in group.Items)
{
Console.WriteLine($"SubGroup.Key: {subGroup.Key}");
Console.WriteLine($"SubGroup.Count: {subGroup.Count}");
}
}
}
In C#,I have List of Employee object. Employee class is
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
}
In List objected are sorted based on Employee.ID. I have an array of int which is basically Employee.ID which I want on top of the list and in list,order must remain same as in array.
If I hava input like this
List:
[
{ID:1,Name:A},
{ID:2,Name:B},
{ID:3,Name:AA},
{ID:4,Name:C},
{ID:5,Name:CD},
.
.
{ID:100,Name:Z}
]
and Array: {2,3,1}
Then I want Output List:
[
{ID:2,Name:B},
{ID:3,Name:AA},
{ID:1,Name:A},
{ID:4,Name:C},
{ID:5,Name:CD},
.
.
{ID:100,Name:Z}
]
And I have done this
foreach (int i in a)
{
list = list.OrderBy(x => x.ID != i).ToList();
}
//a is array
//list is List
Any better Solution.Thanks in advance.
After you got your list sorted based on the ID just iterate the array and move the elements. In order to do this you need to first remove and then insert the item at the correct position.
for(int i = 0; i < myArray.Length; i++)
{
var e = myList.Single(x => x.Id == myArray[i]);
myList.Remove(e);
myList.Insert(i, e);
}
You may also want to use SingleOrDefault instead of Single to verify that myList even contains the element with the current id, e.g. when your array contains [2, 3, 101]
To add another version to the mix. The complete sorting can be done in one go:
list = list.OrderBy(e=> {int i =Array.IndexOf(a, e.ID); return i == -1 ? int.MaxValue : i; }).ToList();
where list is the EmployeeList and a the indices array. (NB, the for loop is not needed, the above should do both sortings).
Inside the OrderBy callback, if the id is not inside a, int.MaxValue is returned to place it after the ones inside the array (a.Length would work as well). OrderBy should maintain the original order of the enumeration (list) for those elements that return the same value.
PS, if you want to sort first by index inside a and the rest on the ids (not necessarily the original order), you can use the following (as long as a.Length + largest ID < int.MaxValue) : list = list.OrderBy(e=> {int i =Array.IndexOf(a, e.ID); return i == -1 ? a.Length + e.ID : i; }).ToList();
Here's a way to do it in pure LINQ, without changing the original sequence.
Broken into steps to see what's going on.
public static void Main()
{
var employeeList = new List<Employee>()
{
new Employee(){ ID= 1,Name= "A"},
new Employee() { ID= 2,Name= "B"},
new Employee() { ID= 3,Name= "AA"},
new Employee() { ID= 4,Name= "C"},
new Employee() { ID= 5,Name= "CD"},
new Employee() { ID= 100,Name= "Z"}
};
var orderByArray = new int[] { 2, 3, 1, 100, 5, 4 };
var sortPos = orderByArray.Select((i, index) => new { ID = i, SortPos = index });
var joinedList = employeeList.Join(sortPos, e => e.ID, sp => sp.ID, (e, sp) => new { ID = e.ID, Name = e.Name, SortPos = sp.SortPos });
var sortedEmployees = joinedList.OrderBy(e => e.SortPos).Select(e => new Employee { ID = e.ID, Name = e.Name });
}
Try this using LINQ:
List<Employee> employees = ...
int[] ids = ...
var orderEmployees = ids.Select(id => employees.Single(employee => employee.ID == id))
.Concat(employees.Where(employee => !ids.Contains(employee.ID)).ToList();
Foreach id in ids array we will grab the matching employee and we will concat to it all the employees that their id does not exist in ids array.
I like to use a special Comparer for that, it seems clearer to me, though a bit more code. It hides the complexity of the sort in the comparer class, and then you can just call it with :
theList.OrderBy(x => x.id, new ListOrderBasedComparer(sortList));
It will sort according to any list passed to the comparer when instantiating, and will put elements not in the "known sort list" at the end.
You can of course adapt it to your special needs.
public class ListOrderBasedComparer: Comparer<int>
{
private List<int> sortList;
public ListOrderBasedComparer(List<int> sortList)
{
// if you want you can make constructor accept arrays and convert it
// (if you find that more convenient)
this.sortList = sortList;
}
public override int Compare(int x, int y)
{
var indexOfX = sortList.FindIndex(a => a == x);
var indexOfY = sortList.FindIndex(a => a == y);
// handle elements not in sortArray : if not in sort array always assume they should be "less than the others" and "equal between them".
if (indexOfX == -1 && indexOfY == -1) return 0;
if (indexOfY == -1) return -1;
if (indexOfX == -1) return 1;
// if elements are in sortArray (FindIndex returned other than -1), use usual comparison of index values
return indexOfX.CompareTo(indexOfY);
}
}
Example on how to use it, with Linq :
public class TestCompare
{
public void test ()
{
var myArray = new MyClass[]
{
new MyClass { id = 1, name = "A" },
new MyClass { id = 2, name = "B" },
new MyClass { id = 3, name = "C" },
new MyClass { id = 4, name = "D" },
new MyClass { id = 5, name = "E" },
new MyClass { id = 6, name = "F" },
};
var myArray2 = new MyClass[]
{
new MyClass { id = 1, name = "A" },
new MyClass { id = 2, name = "B" },
new MyClass { id = 0, name = "X" },
new MyClass { id = 3, name = "C" },
new MyClass { id = 4, name = "D" },
new MyClass { id = 23, name = "Z"},
new MyClass { id = 5, name = "E" },
new MyClass { id = 6, name = "F" },
};
var sortList = new List<int> { 2, 3, 1, 4, 5, 6 };
// good order
var mySortedArray = myArray.OrderBy(x => x.id, new ListOrderBasedComparer(sortList)).ToList();
// good order with elem id 0 and 23 at the end
var mySortedArray2 = myArray2.OrderBy(x => x.id, new ListOrderBasedComparer(sortList)).ToList();
}
}
public class MyClass
{
public int id;
public string name;
}
How can I use LINQ or Lambda instead of nested and multiple foreach statements.
I want to use a better syntax than nested foreach statements to overwrite the initial list with items from the second list.
In the code below:
I want to overwrite initialList with those in secondList that have the same Value. (Remove Red)
Use the items in secondList where Value was the same (Yellow)
New initialList list should include (Green and Yellow)
static void Main(string[] args)
{
int useProd = 2;
int useDomain = 0;
var person1 = new Person() { prodId = 1, Value = "foo", domainId = 0, Name = "Red" };
var person2 = new Person() { prodId = 1, Value = "bar", domainId = 0, Name = "Green" };
var person3 = new Person() { prodId = 1, Value = "foo", domainId = 1, Name = "Yellow" };
var initialList = new List<Person>();
initialList.Add(person1);
initialList.Add(person2);
var secondList = new List<Person>();
secondList.Add(person3);
List<Person> personsToRemove = new List<Person>();
List<Person> personsToUpdate = new List<Person>();
foreach (var pers1 in initialList)
{
foreach (var pers2 in secondList)
{
if (pers1.Value == pers2.Value)
{
personsToRemove.Add(pers1);
personsToUpdate.Add(pers2);
}
}
}
foreach (var remPers in personsToRemove)
{
initialList.Remove(remPers);
}
foreach (var updPers in personsToUpdate)
{
initialList.Add(updPers);
}
foreach (var item in initialList)
{
Console.WriteLine(String.Format("Value: {0}, prodId: {1}, domainId: {2}, Name: {3}", item.Value, item.prodId, item.domainId, item.Name));
}
Console.ReadKey();
}
public class Person
{
public int prodId { get; set; }
public string Value { get; set; }
public int domainId { get; set; }
public string Name { get; set; }
}
Your nested loops are most efficiently represented with a join. In addition, it would be helpful efficiency-wise to not have to do a linear-search on the entire list just to remove an element and then to add a new one in. There is an overload of Enumerable.Select() we can use to embed the item index in the result, so that the element can simply be replaced directly.
Putting it all together, it looks like this:
var join = from p1 in initialList.Select((p, i) => new { Person = p, Index = i })
join p2 in secondList on p1.Person.Value equals p2.Value
select new { Index = p1.Index, Replacement = p2 };
foreach (var item in join.ToList())
{
initialList[item.Index] = item.Replacement;
}
The above code replaces the original code starting with the declarations of the personsToRemove and personsToUpdate lists, and the first three foreach loops (i.e. all but the one that displays the final result).
Notes:
From the initialList, the code synthesizes an anonymous type containing the Person instance and the index of that instance in the list.
The join clause pairs up all of the items from each list where the Value properties are equal.
Important: if there are multiple elements in either list with the same Value property, they are each paired with every other element in the other list having that same Value. I.e. if initialList has two elements having the Value of "foo" and secondList has three such elements, you will wind up with six elements in the resulting join. Your question does not define whether this is possible, nor what you would want to happen if it were, so I've just ignored that possibility here. :)
The join result is projected to a new anonymous type containing the index of the element to be replaced, and the new value.
The query result is materialized by calling ToList(). This is necessary because the join is otherwise deferred and modifying the initialList would invalidate the query.
Of course, in the remaining foreach all that the code then needs to do is assign to the appropriate index position in the list the replacement value determined by the query.
You can use Generics as well. Below is the short code will work for you:
initialList.ForEach(p =>
{
if (secondList.Any(sp => sp.Value == p.Value))
{
initialList.Remove(p);
initialList.Add(secondList.Single(spu => spu.Value == p.Value));
};
});
I have a this list:
List<myobject> list= new List<myobject>();
list.Add(new myobject{name="n1",recordNumber=1});
list.Add(new myobject{name="n2",recordNumber=2});
list.Add(new myobject{name="n3",recordNumber=3});
list.Add(new myobject{name="n4",recordNumber=3});
I'm looking for the fastest way to select distinct objects based on recordNumber, but if there is more than one object with same recordNumber(here recordNumber=3), I want to select object base on its name.(the name provided by paramater)
thanks
It looks like you are really after something like:
Dictionary<int, List<myobject>> myDataStructure;
That allows you to quickly retrieve by record number. If the List<myobject> with that dictionary key contains more than one entry, you can then use the name to select the correct one.
Note that if your list is not terribly long, an O(n) check that just scans the list checking for the recordNumber and name may be fast enough, in the sense that other things happening in your program could obscure the list lookup cost. Consider that possibility before over-optimizing lookup times.
Here's the LINQ way of doing this:
Func<IEnumerable<myobject>, string, IEnumerable<myobject>> getDistinct =
(ms, n) =>
ms
.ToLookup(x => x.recordNumber)
.Select(xs => xs.Skip(1).Any()
? xs.Where(x => x.name == n).Take(1)
: xs)
.SelectMany(x => x)
.ToArray();
I just tested this with a 1,000,000 randomly created myobject list and it produced the result in 106ms. That should be fast enough for most situations.
Are you looking for
class Program
{
static void Main(string[] args)
{
List<myobject> list = new List<myobject>();
list.Add(new myobject { name = "n1", recordNumber = 1 });
list.Add(new myobject { name = "n2", recordNumber = 2 });
list.Add(new myobject { name = "n3", recordNumber = 3 });
list.Add(new myobject { name = "n4", recordNumber = 3 });
//Generates Row Number on the fly
var withRowNumbers = list
.Select((x, index) => new
{
Name = x.name,
RecordNumber = x.recordNumber,
RowNumber = index + 1
}).ToList();
//Generates Row Number with Partition by clause
var withRowNumbersPartitionBy = withRowNumbers
.OrderBy(x => x.RowNumber)
.GroupBy(x => x.RecordNumber)
.Select(g => new { g, count = g.Count() })
.SelectMany(t => t.g.Select(b => b)
.Zip(Enumerable.Range(1, t.count), (j, i) => new { Rn = i, j.RecordNumber, j.Name}))
.Where(i=>i.Rn == 1)
.ToList();
//print the result
withRowNumbersPartitionBy.ToList().ForEach(i => Console.WriteLine("Name = {0} RecordNumber = {1}", i.Name, i.RecordNumber));
Console.ReadKey();
}
}
class myobject
{
public int recordNumber { get; set; }
public string name { get; set; }
}
Result:
Name = n1 RecordNumber = 1
Name = n2 RecordNumber = 2
Name = n3 RecordNumber = 3
Are you looking for a method to do this?
List<myobject> list= new List<myobject>();
list.Add(new myobject{name="n1",recordNumber=1});
list.Add(new myobject{name="n2",recordNumber=2});
list.Add(new myobject{name="n3",recordNumber=3});
list.Add(new myobject{name="n4",recordNumber=3});
public myobject Find(int recordNumber, string name)
{
var matches = list.Where(l => l.recordNumber == recordNumber);
if (matches.Count() == 1)
return matches.Single();
else return matches.Single(m => m.name == name);
}
This will - of course - break if there are multiple matches, or zero matches. You need to write your own edge cases and error handling!
If the name and recordNumber combination is guaranteed to be unique then you can always use Hashset.
You can then use RecordNumber and Name to generate the HashCode by using a method described here.
class myobject
{
//override GetHashCode
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + recordNumber.GetHashCode();
hash = hash * 23 + name.GetHashCode();
return hash;
}
}
//override Equals
}
I've been searching the difference between Select and SelectMany but I haven't been able to find a suitable answer. I need to learn the difference when using LINQ To SQL but all I've found are standard array examples.
Can someone provide a LINQ To SQL example?
SelectMany flattens queries that return lists of lists. For example
public class PhoneNumber
{
public string Number { get; set; }
}
public class Person
{
public IEnumerable<PhoneNumber> PhoneNumbers { get; set; }
public string Name { get; set; }
}
IEnumerable<Person> people = new List<Person>();
// Select gets a list of lists of phone numbers
IEnumerable<IEnumerable<PhoneNumber>> phoneLists = people.Select(p => p.PhoneNumbers);
// SelectMany flattens it to just a list of phone numbers.
IEnumerable<PhoneNumber> phoneNumbers = people.SelectMany(p => p.PhoneNumbers);
// And to include data from the parent in the result:
// pass an expression to the second parameter (resultSelector) in the overload:
var directory = people
.SelectMany(p => p.PhoneNumbers,
(parent, child) => new { parent.Name, child.Number });
Live Demo on .NET Fiddle
Select many is like cross join operation in SQL where it takes the cross product.
For example if we have
Set A={a,b,c}
Set B={x,y}
Select many can be used to get the following set
{ (x,a) , (x,b) , (x,c) , (y,a) , (y,b) , (y,c) }
Note that here we take the all the possible combinations that can be made from the elements of set A and set B.
Here is a LINQ example you can try
List<string> animals = new List<string>() { "cat", "dog", "donkey" };
List<int> number = new List<int>() { 10, 20 };
var mix = number.SelectMany(num => animals, (n, a) => new { n, a });
the mix will have following elements in flat structure like
{(10,cat), (10,dog), (10,donkey), (20,cat), (20,dog), (20,donkey)}
var players = db.SoccerTeams.Where(c => c.Country == "Spain")
.SelectMany(c => c.players);
foreach(var player in players)
{
Console.WriteLine(player.LastName);
}
De Gea
Alba
Costa
Villa
Busquets
...
SelectMany() lets you collapse a multidimensional sequence in a way that would otherwise require a second Select() or loop.
More details at this blog post.
There are several overloads to SelectMany. One of them allows you to keep trace of any relationship between parent and children while traversing the hierarchy.
Example: suppose you have the following structure: League -> Teams -> Player.
You can easily return a flat collection of players. However you may lose any reference to the team the player is part of.
Fortunately there is an overload for such purpose:
var teamsAndTheirLeagues =
from helper in leagues.SelectMany
( l => l.Teams
, ( league, team ) => new { league, team } )
where helper.team.Players.Count > 2
&& helper.league.Teams.Count < 10
select new
{ LeagueID = helper.league.ID
, Team = helper.team
};
The previous example is taken from Dan's IK blog. I strongly recommend you take a look at it.
I understand SelectMany to work like a join shortcut.
So you can:
var orders = customers
.Where(c => c.CustomerName == "Acme")
.SelectMany(c => c.Orders);
The SelectMany() method is used to flatten a sequence in which each of the elements of the sequence is a separate.
I have class user same like this
class User
{
public string UserName { get; set; }
public List<string> Roles { get; set; }
}
main:
var users = new List<User>
{
new User { UserName = "Reza" , Roles = new List<string>{"Superadmin" } },
new User { UserName = "Amin" , Roles = new List<string>{"Guest","Reseption" } },
new User { UserName = "Nima" , Roles = new List<string>{"Nurse","Guest" } },
};
var query = users.SelectMany(user => user.Roles, (user, role) => new { user.UserName, role });
foreach (var obj in query)
{
Console.WriteLine(obj);
}
//output
//{ UserName = Reza, role = Superadmin }
//{ UserName = Amin, role = Guest }
//{ UserName = Amin, role = Reseption }
//{ UserName = Nima, role = Nurse }
//{ UserName = Nima, role = Guest }
You can use operations on any item of sequence
int[][] numbers = {
new[] {1, 2, 3},
new[] {4},
new[] {5, 6 , 6 , 2 , 7, 8},
new[] {12, 14}
};
IEnumerable<int> result = numbers
.SelectMany(array => array.Distinct())
.OrderBy(x => x);
//output
//{ 1, 2 , 2 , 3, 4, 5, 6, 7, 8, 12, 14 }
List<List<int>> numbers = new List<List<int>> {
new List<int> {1, 2, 3},
new List<int> {12},
new List<int> {5, 6, 5, 7},
new List<int> {10, 10, 10, 12}
};
IEnumerable<int> result = numbers
.SelectMany(list => list)
.Distinct()
.OrderBy(x=>x);
//output
// { 1, 2, 3, 5, 6, 7, 10, 12 }
Select is a simple one-to-one projection from source element to a result element. Select-
Many is used when there are multiple from clauses in a query expression: each element in the original sequence is used to generate a new sequence.
The formal description for SelectMany() is:
Projects each element of a sequence to an IEnumerable and flattens
the resulting sequences into one sequence.
SelectMany() flattens the resulting sequences into one sequence, and invokes a result selector function on each element therein.
class PetOwner
{
public string Name { get; set; }
public List<String> Pets { get; set; }
}
public static void SelectManyEx()
{
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" } } };
// Query using SelectMany().
IEnumerable<string> query1 = petOwners.SelectMany(petOwner => petOwner.Pets);
Console.WriteLine("Using SelectMany():");
// Only one foreach loop is required to iterate
// through the results since it is a
// one-dimensional collection.
foreach (string pet in query1)
{
Console.WriteLine(pet);
}
// This code shows how to use Select()
// instead of SelectMany().
IEnumerable<List<String>> query2 =
petOwners.Select(petOwner => petOwner.Pets);
Console.WriteLine("\nUsing Select():");
// Notice that two foreach loops are required to
// iterate through the results
// because the query returns a collection of arrays.
foreach (List<String> petList in query2)
{
foreach (string pet in petList)
{
Console.WriteLine(pet);
}
Console.WriteLine();
}
}
/*
This code produces the following output:
Using SelectMany():
Scruffy
Sam
Walker
Sugar
Scratches
Diesel
Using Select():
Scruffy
Sam
Walker
Sugar
Scratches
Diesel
*/
The main difference is the result of each method while SelectMany() returns a flattern results; the Select() returns a list of list instead of a flattern result set.
Therefor the result of SelectMany is a list like
{Scruffy, Sam , Walker, Sugar, Scratches , Diesel}
which you can iterate each item by just one foreach. But with the result of select you need an extra foreach loop to iterate through the results because the query returns a collection of arrays.
Some SelectMany may not be necessary. Below 2 queries give the same result.
Customers.Where(c=>c.Name=="Tom").SelectMany(c=>c.Orders)
Orders.Where(o=>o.Customer.Name=="Tom")
For 1-to-Many relationship,
if Start from "1", SelectMany is needed, it flattens the many.
if Start from "Many", SelectMany is not needed. (still be able to filter from "1", also this is simpler than below standard join query)
from o in Orders
join c in Customers on o.CustomerID equals c.ID
where c.Name == "Tom"
select o
Just for an alternate view that may help some functional programmers out there:
Select is map
SelectMany is bind (or flatMap for your Scala/Kotlin people)
Without getting too technical - database with many Organizations, each with many Users:-
var orgId = "123456789";
var userList1 = db.Organizations
.Where(a => a.OrganizationId == orgId)
.SelectMany(a => a.Users)
.ToList();
var userList2 = db.Users
.Where(a => a.OrganizationId == orgId)
.ToList();
both return the same ApplicationUser list for the selected Organization.
The first "projects" from Organization to Users, the second queries the Users table directly.
It's more clear when the query return a string (an array of char):
For example if the list 'Fruits' contains 'apple'
'Select' returns the string:
Fruits.Select(s=>s)
[0]: "apple"
'SelectMany' flattens the string:
Fruits.SelectMany(s=>s)
[0]: 97 'a'
[1]: 112 'p'
[2]: 112 'p'
[3]: 108 'l'
[4]: 101 'e'
Consider this example :
var array = new string[2]
{
"I like what I like",
"I like what you like"
};
//query1 returns two elements sth like this:
//fisrt element would be array[5] :[0] = "I" "like" "what" "I" "like"
//second element would be array[5] :[1] = "I" "like" "what" "you" "like"
IEnumerable<string[]> query1 = array.Select(s => s.Split(' ')).Distinct();
//query2 return back flat result sth like this :
// "I" "like" "what" "you"
IEnumerable<string> query2 = array.SelectMany(s => s.Split(' ')).Distinct();
So as you see duplicate values like "I" or "like" have been removed from query2 because "SelectMany" flattens and projects across multiple sequences.
But query1 returns sequence of string arrays. and since there are two different arrays in query1 (first and second element), nothing would be removed.
The SelectMany method knocks down an IEnumerable<IEnumerable<T>> into an IEnumerable<T>, like communism, every element is behaved in the same manner(a stupid guy has same rights of a genious one).
var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }
One more example how SelectMany + Select can be used in order to accumulate sub array objects data.
Suppose we have users with they phones:
class Phone {
public string BasePart = "555-xxx-xxx";
}
class User {
public string Name = "Xxxxx";
public List<Phone> Phones;
}
Now we need to select all phones' BaseParts of all users:
var usersArray = new List<User>(); // array of arrays
List<string> allBaseParts = usersArray.SelectMany(ua => ua.Phones).Select(p => p.BasePart).ToList();
Suppose you have an array of countries
var countries = new[] { "France", "Italy" };
If you perform Select on countries, you will get each element of the array as IEnumerable<T>
IEnumerable<string> selectQuery = countries.Select(country => country);
In the above code, the country represents a string that refers to each country in the array. now iterate over selectQuery to get countries:
foreach(var country in selectQuery)
Console.WriteLine(country);
// output
//
// France
// Italy
If you want to print every character of countries you have to use nested foreach
foreach (var country in selectQuery)
{
foreach (var charOfCountry in country)
{
Console.Write(charOfCountry + ", ");
}
}
// output
// F, r, a, n, c, e, I, t, a, l, y,
OK. now try to perform SelectMany on countries. This time SelectMany gets each country as string (as before) and because of string type is a collection of chars, SelectMany tries to divide each country into its constituent parts (chars) and then returns a collection of chars as IEnumerable<T>
IEnumerable<char> selectManyQuery = countries.SelectMany(country => country);
In the above code, the country represents a string that refers to each country in the array as before, but the return value is the chars of each country
Actually SelectMany likes to fetch two levels inside of collections and flatten the second level as IEnumerable<T>
Now iterate over selectManyQuery to get chars of each country:
foreach(var charOfCountry in selectManyQuery)
Console.Write(charOfCountry + ", ");
// output
// F, r, a, n, c, e, I, t, a, l, y,
Here is a code example with an initialized small collection for testing:
class Program
{
static void Main(string[] args)
{
List<Order> orders = new List<Order>
{
new Order
{
OrderID = "orderID1",
OrderLines = new List<OrderLine>
{
new OrderLine
{
ProductSKU = "SKU1",
Quantity = 1
},
new OrderLine
{
ProductSKU = "SKU2",
Quantity = 2
},
new OrderLine
{
ProductSKU = "SKU3",
Quantity = 3
}
}
},
new Order
{
OrderID = "orderID2",
OrderLines = new List<OrderLine>
{
new OrderLine
{
ProductSKU = "SKU4",
Quantity = 4
},
new OrderLine
{
ProductSKU = "SKU5",
Quantity = 5
}
}
}
};
//required result is the list of all SKUs in orders
List<string> allSKUs = new List<string>();
//With Select case 2 foreach loops are required
var flattenedOrdersLinesSelectCase = orders.Select(o => o.OrderLines);
foreach (var flattenedOrderLine in flattenedOrdersLinesSelectCase)
{
foreach (OrderLine orderLine in flattenedOrderLine)
{
allSKUs.Add(orderLine.ProductSKU);
}
}
//With SelectMany case only one foreach loop is required
allSKUs = new List<string>();
var flattenedOrdersLinesSelectManyCase = orders.SelectMany(o => o.OrderLines);
foreach (var flattenedOrderLine in flattenedOrdersLinesSelectManyCase)
{
allSKUs.Add(flattenedOrderLine.ProductSKU);
}
//If the required result is flattened list which has OrderID, ProductSKU and Quantity,
//SelectMany with selector is very helpful to get the required result
//and allows avoiding own For loops what according to my experience do code faster when
// hundreds of thousands of data rows must be operated
List<OrderLineForReport> ordersLinesForReport = (List<OrderLineForReport>)orders.SelectMany(o => o.OrderLines,
(o, ol) => new OrderLineForReport
{
OrderID = o.OrderID,
ProductSKU = ol.ProductSKU,
Quantity = ol.Quantity
}).ToList();
}
}
class Order
{
public string OrderID { get; set; }
public List<OrderLine> OrderLines { get; set; }
}
class OrderLine
{
public string ProductSKU { get; set; }
public int Quantity { get; set; }
}
class OrderLineForReport
{
public string OrderID { get; set; }
public string ProductSKU { get; set; }
public int Quantity { get; set; }
}
A select operator is used to select value from a collection and SelectMany operator is used to selecting values from a collection of collection i.e. nested collection.
It is the best way to understand i think.
var query =
Enumerable
.Range(1, 10)
.SelectMany(ints => Enumerable.Range(1, 10), (a, b) => $"{a} * {b} = {a * b}")
.ToArray();
Console.WriteLine(string.Join(Environment.NewLine, query));
Console.Read();
Multiplication Table example.