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;
});
Related
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)).
I would like to do something like this (below) but not sure if there is a formal/optimized syntax to do so?
.Orderby(i => i.Value1)
.Take("Bottom 100 & Top 100")
.Orderby(i => i.Value2);
basically, I want to sort by one variable, then take the top 100 and bottom 100, and then sort those results by another variable.
Any suggestions?
var sorted = list.OrderBy(i => i.Value);
var top100 = sorted.Take(100);
var last100 = sorted.Reverse().Take(100);
var result = top100.Concat(last100).OrderBy(i => i.Value2);
I don't know if you want Concat or Union at the end. Concat will combine all entries of both lists even if there are similar entries which would be the case if your original list contains less than 200 entries. Union would only add stuff from last100 that is not already in top100.
Some things that are not clear but that should be considered:
If list is an IQueryable to a db, it probably is advisable to use ToArray() or ToList(), e.g.
var sorted = list.OrderBy(i => i.Value).ToArray();
at the beginning. This way only one query to the database is done while the rest is done in memory.
The Reverse method is not optimized the way I hoped for, but it shouldn't be a problem, since ordering the list is the real deal here. For the record though, the skip method explained in other answers here is probably a little bit faster but needs to know the number of elements in list.
If list would be a LinkedList or another class implementing IList, the Reverse method could be done in an optimized way.
You can use an extension method like this:
public static IEnumerable<T> TakeFirstAndLast<T>(this IEnumerable<T> source, int count)
{
var first = new List<T>();
var last = new LinkedList<T>();
foreach (var item in source)
{
if (first.Count < count)
first.Add(item);
if (last.Count >= count)
last.RemoveFirst();
last.AddLast(item);
}
return first.Concat(last);
}
(I'm using a LinkedList<T> for last because it can remove items in O(1))
You can use it like this:
.Orderby(i => i.Value1)
.TakeFirstAndLast(100)
.Orderby(i => i.Value2);
Note that it doesn't handle the case where there are less then 200 items: if it's the case, you will get duplicates. You can remove them using Distinct if necessary.
Take the top 100 and bottom 100 separately and union them:
var tempresults = yourenumerable.OrderBy(i => i.Value1);
var results = tempresults.Take(100);
results = results.Union(tempresults.Skip(tempresults.Count() - 100).Take(100))
.OrderBy(i => i.Value2);
You can do it with in one statement also using this .Where overload, if you have the number of elements available:
var elements = ...
var count = elements.Length; // or .Count for list
var result = elements
.OrderBy(i => i.Value1)
.Where((v, i) => i < 100 || i >= count - 100)
.OrderBy(i => i.Value2)
.ToArray(); // evaluate
Here's how it works:
| first 100 elements | middle elements | last 100 elements |
i < 100 i < count - 100 i >= count - 100
You can write your own extension method like Take(), Skip() and other methods from Enumerable class. It will take the numbers of elements and the total length in list as input. Then it will return first and last N elements from the sequence.
var result = yourList.OrderBy(x => x.Value1)
.GetLastAndFirst(100, yourList.Length)
.OrderBy(x => x.Value2)
.ToList();
Here is the extension method:
public static class SOExtensions
{
public static IEnumerable<T> GetLastAndFirst<T>(
this IEnumerable<T> seq, int number, int totalLength
)
{
if (totalLength < number*2)
throw new Exception("List length must be >= (number * 2)");
using (var en = seq.GetEnumerator())
{
int i = 0;
while (en.MoveNext())
{
i++;
if (i <= number || i >= totalLength - number)
yield return en.Current;
}
}
}
}
I'm trying to find out what's a good way to continually iterate through a dbset over various function calls, looping once at the end.
Essentially I've got a bunch of Ads in the database, and I want to getNext(count) on the dbset.Ads
Here's an example
ID Text Other Columns...
1 Ad1 ...
2 Ad2 ...
3 Ad3 ...
4 Ad4 ...
5 Ad5 ...
6 Ad6 ...
Let's say that in my View, I determine I need 2 ads to display for User 1. I want to return Ads 1-2. User 2 then requests 3 ads. I want it to return 3-5. User 3 needs 2 ads, and the function should return ads 6 and 1, looping back to the beginning.
Here's my code that I've been working with (it's in class AdsManager):
Ad NextAd = db.Ads.First();
public IEnumerable<Ad> getAds(count)
{
var output = new List<Ad>();
IEnumerable<Ad> Ads = db.Ads.OrderBy(x=>x.Id).SkipWhile(x=>x.Id != NextAd.Id);
output.AddRange(Ads);
//If we're at the end, handle that case
if(output.Count != count)
{
NextAd = db.Ads.First();
output.AddRange(getAds(count - output.Count));
}
NextAd = output[count-1];
return output;
}
The problem is that the function call IEnumerable<Ad> Ads = db.Ads.OrderBy(x=>x.Id).SkipWhile(x=>x.Id != NextAd.Id); throws an error on AddRange(Ads):
LINQ to Entities does not recognize the method 'System.Linq.IQueryable'1[Domain.Entities.Ad] SkipWhile[Ad](System.Linq.IQueryable'1[Domain.Entities.Ad], System.Linq.Expressions.Expression'1[System.Func`2[Domain.Entities.Ad,System.Boolean]])' method, and this method cannot be translated into a store expression.
I originally had loaded the entire dbset into a Queue, and did enqueue/dequeue, but that would not updat when a change was made to the database. I got the idea for this algorithm based on Get the next and previous sql row by Id and Name, EF?
What call should I be making to the database to get what I want?
UPDATE: Here's the working Code:
public IEnumerable<Ad> getAds(int count)
{
List<Ad> output = new List<Ad>();
output.AddRange(db.Ads.OrderBy(x => x.Id).Where(x => x.Id >= NextAd.Id).Take(count + 1));
if(output.Count != count+1)
{
NextAd = db.Ads.First();
output.AddRange(db.Ads.OrderBy(x => x.Id).Where(x => x.Id >= NextAd.Id).Take(count - output.Count+1));
}
NextAd = output[count];
output.RemoveAt(count);
return output;
}
SkipWhile is not supported in the EF; it can't be translated into SQL code. EF basically works on sets and not sequences (I hope that sentence makes sense).
A workaround is to simply use Where, e.g.:
IEnumerable<Ad> Ads = db.Ads.OrderBy(x=>x.Id).Where(x=>x.Id >= NextAd.Id);
Maybe you can simplify this into something like this:
Ad NextAd = db.Ads.First();
public IQueryable<Ad> getAds(count)
{
var firstTake = db.Ads
.OrderBy(x => x.Id)
.Where(x => x.Id >= NextAd.Id);
var secondTake = db.Ads
.OrderBy(x => x.Id)
.Take(count - result.Count());
var result = firstTake.Concat(secondTake);
NextAd = result.LastOrDefault()
return result;
}
Sorry, haven't tested this, but it should work.
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();
I have a generic list
Simplified example
var list = new List<string>()
{
"lorem1.doc",
"lorem2.docx",
"lorem3.ppt",
"lorem4.pptx",
"lorem5.doc",
"lorem6.doc",
};
What I would like to do is to sort these items based on an external list ordering
In example
var sortList = new[] { "pptx", "ppt", "docx", "doc" };
// Or
var sortList = new List<string>() { "pptx", "ppt", "docx", "doc" };
Is there anything built-in to linq that could help me achieve this or do I have to go the foreach way?
With the list you can use IndexOf for Enumerable.OrderBy:
var sorted = list.OrderBy(s => sortList.IndexOf(Path.GetExtension(s)));
So the index of the extension in the sortList determines the priority in the other list. Unknown extensions have highest priority since their index is -1.
But you need to add a dot to the extension to get it working:
var sortList = new List<string>() { ".pptx", ".ppt", ".docx", ".doc" };
If that's not an option you have to fiddle around with Substring or Remove, for example:
var sorted = list.OrderBy(s => sortList.IndexOf(Path.GetExtension(s).Remove(0,1)));
This solution will work even if some file names do not have extensions:
var sortList = new List<string>() { "pptx", "ppt", "docx", "doc" };
var list = new List<string>()
{
"lorem1.doc",
"lorem2.docx",
"lorem3.ppt",
"lorem4.pptx",
"lorem5.doc",
"lorem6.doc",
};
var result =
list.OrderBy(f => sortList.IndexOf(Path.GetExtension(f).Replace(".","")));
You could try using Array.IndexOf() method:
var sortedList = list.OrderBy(i => sortList.IndexOf(System.IO.Path.GetExtension(i))).ToList();
A sortDicionary would be more efficient:
var sortDictionary = new Dictionary<string, int> {
{ ".pptx", 0 },
{ ".ppt" , 1 },
{ ".docx", 2 },
{ ".doc" , 3 } };
var sortedList = list.OrderBy(i => {
var s = Path.GetExtension(i);
int rank;
if (sortDictionary.TryGetValue(s, out rank))
return rank;
return int.MaxValue; // for unknown at end, or -1 for at start
});
This way the lookup is O(1) rather than O(# of extensions).
Also, if you have a large number of filenames and a small number of extensions, it might actually be faster to do
var sortedList = list
.GroupBy(p => Path.GetExtension(p))
.OrderBy(g => {
int rank;
if (sortDictionary.TryGetValue(g.Key, out rank))
return rank;
return int.MaxValue; // for unknown at end, or -1 for at start
})
.SelectMany(g => g);
This means the sort scales by the number of distinct extensions in the input, rather than the number of items in the input.
This also allows you to give two extensions the same priority.
Here's another way that does not use OrderBy:
var res =
sortList.SelectMany(x => list.Where(f => Path.GetExtension(f).EndsWith(x)));
Note that the complexity of this approach is O(n * m) with n = sortList.Count and m list.Count.
The OrderBy approach worst-case complexity is instead O(n * m * log m) but probably in general it will be faster (since IndexOf does not result always in O(n) ). However with small n and m you won't notice any difference.
For big lists the fastest way ( complexity O(n+m) ) could be constructing a temporary lookup i.e. :
var lookup = list.ToLookup(x => Path.GetExtension(x).Remove(0,1));
var res = sortList.Where(x => lookup.Contains(x)).SelectMany(x => lookup[x]);