I would like to split a list into 'n' amount of sub-lists.
I have a list of Form teachers and a list of Students. Every Student is assigned to one Form teacher and each Form teacher can have more than one Student. The list of Form teachers is dynamic - it is being populated based on checkbox selection on the form (i.e: there may be one, three, six, etc. in the list).
//A method to assign the Selected Form teachers to the Students
private void AssignFormTeachers(List<FormTeacher> formTeacherList, List<Student> studentList)
{
int numFormTeachers = formTeacherList.Count;
//Sort the Students by Course - this ensures cohort identity.
studentList = studentList.OrderBy(Student => Student.CourseID).ToList();
//Split the list according to the number of Form teachers
List<List<Student>> splitStudentList = splitList(numFormTeachers , studentList);
The splitList() method is where I'm attempting to split the list into a list of Student lists, but I'm having a problem. Let's say there are 3 Form teachers - I can't seem to split the list into 3 sub-lists, but rather end up with lists of 3 Students.
I would really appreciate some help with this. I have searched for a possible solution, but every time I end up with lists of size 'n', rather than 'n' amount of lists. If this question has been answered before, please point me in the direction of that answer.
You're trying to partition your list into n parts with equal number of elements?
Try GroupBy:
var splitStudentList = studentList.Select((s, i) => new { s, i })
.GroupBy(x => x.i % numFormTeachers)
.Select(g => g.Select(x => x.s).ToList())
.ToList();
Or you can create your own extension method to do that. I've described how to do it right on my blog: Partitioning the collection using LINQ: different approaches, different performance, the same result.
public IEnumerable<IEnumerable<T>> Partition<T>(IEnumerable<T> source, int size)
{
var partition = new List<T>(size);
var counter = 0;
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
partition.Add(enumerator.Current);
counter++;
if (counter % size == 0)
{
yield return partition.ToList();
partition.Clear();
counter = 0;
}
}
if (counter != 0)
yield return partition;
}
}
usage:
var splitStudentList = studentList.Partition(numFormTeachers)
.Select(x => x.ToList())
.ToList();
Related
So suppose we have a parking(represented as a dictionary<int,bool> :
Every parking lot has its id and a boolean(free,filled).
This way:
Dictionary<int,bool> parking..
parking[0]= true // means that the first parking lot is free
My question is i want to get the all sublist of consecutive elements that matchs in a condition : parking-lot is free.
First i can get elements that fits in this condition easy:
parking.Where(X => X.Value).Select(x => x.Key).ToList();
But then using linq operations i dont know how to get the first generated list that matchs in.
Can i do this without thousand of foreach-while loops checking iterating one by one, is there a easier way with linq?
This method gets a list of consecutive free parking lots
data:
0-free,
1-free,
2-filled ,
3-free
The results will be two lists:
First One will contain => 0 ,1
Second One will contain=> 3
These are the list of consecutive of parking lots that are free.
public List<List<int>> ConsecutiveParkingLotFree(int numberOfConsecutive){}
You can always write your own helper function to do things like this. For example
public static IEnumerable<List<T>> GroupSequential<T, TKey>(
this IEnumerable<T> self,
Func<T, bool> condition)
{
var list = new List<T>();
using var enumerator = self.GetEnumerator();
if (enumerator.MoveNext())
{
var current = enumerator.Current;
var oldValue = condition(current);
if (oldValue)
{
list.Add(current);
}
while (enumerator.MoveNext())
{
current = enumerator.Current;
var newValue = condition(current);
if (newValue)
{
list.Add(current);
}
else if (oldValue)
{
yield return list;
list = new List<T>();
}
oldValue = newValue;
}
if (list.Count > 0)
{
yield return list;
}
}
}
This will put all the items with a true-value in a list. When a true->false transition is encountered the list is returned and recreated. I would expect that there are more compact ways to write functions like this, but it should do the job.
You can apply GroupWhile solution here.
parking.Where(X => X.Value)
.Select(x => x.Key)
.GroupWhile((x, y) => y - x == 1)
.ToList()
I am trying to make a program that checks if a given sudoku board is valid (solved correctly).
Also want to do it using linq however I find it hard to come up with a solution to get all the 3x3 groups from the board.
I want to get them as a IEnumerable<IEnumerable<int>> because of how I wrote the rest of the code.
Here is my solution so far :
public static bool IsValidSudoku(IEnumerable<IEnumerable<int>> sudokuBoard)
{
if (sudokuBoard == null)
{
throw new ArgumentNullException();
}
var columns = Enumerable.Range(0, 9)
.Select(lineCount => Enumerable.Range(0,9)
.Select(columnCount=>sudokuBoard
.ElementAt(columnCount)
.ElementAt(lineCount)
));
var groups = //this is where I got stuck
return columns.All(IsValidGroup) &&
sudokuBoard.All(IsValidGroup) &&
groups.All(IsValidGroup);
}
static bool IsValidGroup(IEnumerable<int> group)
{
return group.Distinct().Count() == group.Count()&&
group.All(x => x <= 9 && x > 0)&&
group.Count() == 9;
}
Performance is not important here.
Thank you for any advice!
You need two enumerables to choose which 3x3 group you're selecting, and then you can use .Skip and .Take to take runs of three elements to fetch those groups.
var groups = Enumerable.Range(0, 3).SelectMany(gy =>
Enumerable.Range(0, 3).Select(gx =>
// We now have gx and gy 0-2; find the three rows we want
sudoBoard.Skip(gy * 3).Take(3).Select(row =>
// and from each row take the three columns
row.Skip(gx * 3).Take(3)
)
));
This should give you an IEnumerable of IEnumerable<IEnumerable<int>>s as requested. However to pass each group to IsValidGroup you'll have to flatten the 3x3 IEnumerable<IEnumerable<int>> into a 9-longIEnumerable<int>s, e.g. groups.Select(group => group.SelectMany(n => n)).
We have the list A which contain the random indexes in it. I have another list B which contains the class objects in it. I want to parse list B with the indexes present in list A and find the objects which have name Amar in it using Linq.
For example:
List<int> Indexes = new List<int>(); // This contains the random indexes
List<Student> StuObj = new List<Student>();
Class Student
{
String name;
}
Now I want to parse the list StuObj with the respect to the indexes present in the list Indexes and get the Student object indexes present in the list StuObj where the name is Amar.
You can do that using Linq. The Where has an overload that provides the index of the element:
List<int> indexes = new List<int>() { 5, 1 , 10, 30 };
var results = listB.Where((item, index)=> indexes.Contains(index)
&& item.Name == "Amar")
.Select(x => listB.IndexOf(x)).ToList();
Edit: to get the index of the element in the original listB, you can make use of the IndexOf(T) method.
This should work:
var result = Indexes
.Select(i => StuObj[i])
.Where(s => s.name = "Amar").ToList()
It performs fast index lookup to fetch only required objects. If you know there is only one record, you can use First or FirstOrDefault instead of ToList.
Assuming you have two lists, List indexList and List dataList, you can use select as follows:
indexList.Select(i => dataList[i]);
You should consider what you wish to happen if indexList contains an integer < 0 or > the size of dataList. For example, you could replace invalid entries with null, like:
indexList.Select(i => i < 0 || i >= dataList.Count ? null : dataList[i]);
Or you could filter them out like:
indexList.Where(i => i>=0 && i < dataList.Count).Select(i => dataList[i]);
Or you may know via preconditions that you will never have items in the index list that are out of the range of valid values.
EDIT
Based on the updated question, try this:
dataList.Where((item, index) => indexList.Contains(index) && item.Name == "Amar")
.Select(item => dataList.IndexOf(item));
This takes advantage of the Select and Where overloads that take the index of the item. The Where clause selects the item where the item's index in dataList is in the indexList, and also where the item's Name is Amar. The Select clause then returns the index of the item in the original data list.
Something like this:
var result = listA
.Where(i => i >= 0 && i < listB.Count)
.Select(i => listB[i])
.FirstOrDefault(b => b.Name == "Amar");
Basically you use the value from listA as index of an element of the listB. If you are sure that listA contains only valid indexes, then Where call can be removed.
EDIT: As per updated question, the answer is even easier:
var result = listA
.Where(i => i >= 0 && i < listB.Count && listB[i].Name == "Amar")
.ToList();
I see absolutely no reason to use the linear searching (hence slow) methods Contains and IndexOf as suggested in some other answers.
I need to optimize the below foreach loop. The foreach loop is taken more time to get the unique items.
Instead can the FilterItems be converted into a list collection. If so how to do it. Then i will take unique items easily from it.
The problem arises when i have 5,00,000 items in FilterItems.
Please suggest some ways to optimize the below code:
int i = 0;
List<object> order = new List<object>();
List<object> unique = new List<object>();
// FilterItems IS A COLLECTION OF RECORDS. CAN THIS BE CONVERTED TO A LIST COLLECTION DIRECTLY, SO THAT I CAN TAKE THE UNIQUE ITEMS FROM IT.
foreach (Record rec in FilterItems)
{
string text = rec.GetValue(“Column Name”);
int position = order.BinarySearch(text);
if (position < 0)
{
order.Insert(-position - 1, text);
unique.Add(text);
}
i++;
}
It's unclear what you mean by "converting FilterItems into a list" when we don't know anything about it, but you could definitely consider sorting after you've got all the items, rather than as you go:
var strings = FilterItems.Select(record => record.GetValue("Column Name"))
.Distinct()
.OrderBy(x => x)
.ToList();
The use of Distinct() here will avoid sorting lots of equal items - it looks like you only want distinct items anyway.
If you want unique to be in the original order but order to be the same items, just sorted, you could use:
var unique = FilterItems.Select(record => record.GetValue("Column Name"))
.Distinct()
.ToList();
var order = unique.OrderBy(x => x).ToList();
Now Distinct() isn't guaranteed to preserve order - but it does so in the current implementation, and that's the most natural implementation, too.
I have a list of data that I want to display as a table in a GUI (HTML).
I want to create a lambda expression so that a list of hundred items is for instance divided into 20 rows, 5 items each. Can I create a concise lambda expression to do this?
This is what I came up with (yes, 5 is a magic number, it is the number of items per row):
bool isCreateNewRow = true;
var aggregate = Model.Aggregate(new Collection<ICollection<Album>>(),
(tableCollection, album) =>
{
if (isCreateNewRow)
{
tableCollection.Add(new Collection<Album>());
isCreateNewRow = false;
}
tableCollection.Last().Add(album);
if(tableCollection.Last().Count() >= 5)
{
isCreateNewRow = true;
}
return tableCollection;
});
Is there a shorter way to create a 2 dimensional datastructure (IEnumerables of IEnumerables)?
It would be so much easier to
a) create your (1D) resultset
b) use 1 or 2 for loops to process (present) those results in table form.
Also because those 2 steps would logically belong to different layers of a solution.
// Create 20 dummy items.
var albums = Enumerable.Range(1, 20)
.Select(i => string.Format("Album {0}", i));
// Associate each one with the row index.
var numbered = albums
.Select((item, index) =>
new { Row = index / 3, Item = item });
// Arrange into rows.
var table = numbered
.GroupBy(x => x.Row)
.Select(g => g.Select(x=>x.Item).AsEnumerable());
At this point, table is an IEnumerable<IEnumerable<string>>.
To turn it into HTML, try this:
var html = rows.Aggregate(
new StringBuilder("<table>"),
(tableBuilder, row) => {
tableBuilder.AppendFormat("<tr>{0}</tr>",
row.Aggregate(new StringBuilder(),
(rowBuilder, cell) => {
rowBuilder.AppendFormat("<td>{0}</td>", cell);
return rowBuilder;
}));
return tableBuilder;
},
(tableBuilder) => {
tableBuilder.Append("</table>");
return tableBuilder;
});