Error when trying to split list into smaller lists - c#

The Error:
cannot convert from 'System.Collections.Generic.List<System.Collections.Generic.IEnumerable>' to 'System.Collections.Generic.List'
The Code:
for (int i = 0; i < Speed; i++)
{
Tasks[i] = Task.Run(() =>
{
var arr_ = arr.Chunk(Total / Speed).ToList();
Program.Check(arr_, Key, Current, Total, Node, Token);
}, Token);
}
Chunk(int) Method:
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> list, int chunkSize)
{
if (chunkSize <= 0)
{
throw new ArgumentException("chunkSize must be greater than 0.");
}
while (list.Any())
{
yield return list.Take(chunkSize);
list = list.Skip(chunkSize);
}
}
I've been stuck here for a while now without a solution, can any of you tell me what I'm doing wrong? The idea is to make go from a bigger list (arr) and convert it into smaller lists of Total / Speed size in a loop which then uses it for another function.
The way I understood how the yield return works is that every time you call it it's supposed to return the next iteration of the loop it is in, but I'm not so sure that's exactly how it functions or else it looks like it should work here.
Any help is appreciated, thanks

Chunk() returns an IEnumerable of IEnumerable<T>. You're trying to covert that to a List<T>, which can never work.
You take a list of stuff, break it into smaller lists of the stuff, and attempt to put all those smaller lists back into one big list.
If you really do intend to merge the sub-lists back into a single list, you can do it like this:
var _list = list.Chunk(3).SelectMany(i => i).ToList();
If you consume Chunk() properly based on its return type, it works just fine, e.g.
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6 };
foreach (var chunk in list.Chunk(3))
Console.WriteLine(string.Join(", ", chunk));
outputs
1, 2, 3
4, 5, 6

Related

Variable number of for loops without recursion but with Stack?

I know the usual approach for "variable number of for loops" is said to use a recursive method. But I wonder if I could solve that without recursion and instead with using Stack, since you can bypass recursion with the use of a stack.
My example:
I have a variable number of collections and I need to combine every item of every collection with every other item of the other collections.
// example for collections A, B and C:
A (4 items) + B (8 items) + C (10 items)
4 * 8 * 10 = 320 combinations
I need to run through all those 320 combinations. Yet at compile time I don't know if B or C or D exist. How would a solution with no recursive method but with the use of an instance of Stack look like?
Edit:
I realized Stack is not necessary here at all, while you can avoid recursion with a simple int array and a few while loops. Thanks for help and info.
Not with a stack but without recursion.
void Main()
{
var l = new List<List<int>>()
{
new List<int>(){ 1,2,3 },
new List<int>(){ 4,5,6 },
new List<int>(){ 7,8,9 }
};
var result = CartesianProduct(l);
}
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>()};
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item})
);
}
Function taken form Computing a Cartesian Product with LINQ
Here is an example of how to do this. Algorithm is taken from this question - https://stackoverflow.com/a/2419399/5311735 and converted to C#. Note that it can be made more efficient, but I converted inefficient version to C# because it's better illustrates the concept (you can see more efficient version in the linked question):
static IEnumerable<T[]> CartesianProduct<T>(IList<IList<T>> collections) {
// this contains the indexes of elements from each collection to combine next
var indexes = new int[collections.Count];
bool done = false;
while (!done) {
// initialize array for next combination
var nextProduct = new T[collections.Count];
// fill it
for (int i = 0; i < collections.Count; i++) {
var collection = collections[i];
nextProduct[i] = collection[indexes[i]];
}
yield return nextProduct;
// now we need to calculate indexes for the next combination
// for that, increase last index by one, until it becomes equal to the length of last collection
// then increase second last index by one until it becomes equal to the length of second last collection
// and so on - basically the same how you would do with regular numbers - 09 + 1 = 10, 099 + 1 = 100 and so on.
var j = collections.Count - 1;
while (true) {
indexes[j]++;
if (indexes[j] < collections[j].Count) {
break;
}
indexes[j] = 0;
j--;
if (j < 0) {
done = true;
break;
}
}
}
}

Get all possible distinct triples using LINQ

I have a List contains these values: {1, 2, 3, 4, 5, 6, 7}. And I want to be able to retrieve unique combination of three. The result should be like this:
{1,2,3}
{1,2,4}
{1,2,5}
{1,2,6}
{1,2,7}
{2,3,4}
{2,3,5}
{2,3,6}
{2,3,7}
{3,4,5}
{3,4,6}
{3,4,7}
{3,4,1}
{4,5,6}
{4,5,7}
{4,5,1}
{4,5,2}
{5,6,7}
{5,6,1}
{5,6,2}
{5,6,3}
I already have 2 for loops that able to do this:
for (int first = 0; first < test.Count - 2; first++)
{
int second = first + 1;
for (int offset = 1; offset < test.Count; offset++)
{
int third = (second + offset)%test.Count;
if(Math.Abs(first - third) < 2)
continue;
List<int> temp = new List<int>();
temp .Add(test[first]);
temp .Add(test[second]);
temp .Add(test[third]);
result.Add(temp );
}
}
But since I'm learning LINQ, I wonder if there is a smarter way to do this?
UPDATE: I used this question as the subject of a series of articles starting here; I'll go through two slightly different algorithms in that series. Thanks for the great question!
The two solutions posted so far are correct but inefficient for the cases where the numbers get large. The solutions posted so far use the algorithm: first enumerate all the possibilities:
{1, 1, 1 }
{1, 1, 2 },
{1, 1, 3 },
...
{7, 7, 7}
And while doing so, filter out any where the second is not larger than the first, and the third is not larger than the second. This performs 7 x 7 x 7 filtering operations, which is not that many, but if you were trying to get, say, permutations of ten elements from thirty, that's 30 x 30 x 30 x 30 x 30 x 30 x 30 x 30 x 30 x 30, which is rather a lot. You can do better than that.
I would solve this problem as follows. First, produce a data structure which is an efficient immutable set. Let me be very clear what an immutable set is, because you are likely not familiar with them. You normally think of a set as something you add items and remove items from. An immutable set has an Add operation but it does not change the set; it gives you back a new set which has the added item. The same for removal.
Here is an implementation of an immutable set where the elements are integers from 0 to 31:
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System;
// A super-cheap immutable set of integers from 0 to 31 ;
// just a convenient wrapper around bit operations on an int.
internal struct BitSet : IEnumerable<int>
{
public static BitSet Empty { get { return default(BitSet); } }
private readonly int bits;
private BitSet(int bits) { this.bits = bits; }
public bool Contains(int item)
{
Debug.Assert(0 <= item && item <= 31);
return (bits & (1 << item)) != 0;
}
public BitSet Add(int item)
{
Debug.Assert(0 <= item && item <= 31);
return new BitSet(this.bits | (1 << item));
}
public BitSet Remove(int item)
{
Debug.Assert(0 <= item && item <= 31);
return new BitSet(this.bits & ~(1 << item));
}
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
public IEnumerator<int> GetEnumerator()
{
for(int item = 0; item < 32; ++item)
if (this.Contains(item))
yield return item;
}
public override string ToString()
{
return string.Join(",", this);
}
}
Read this code carefully to understand how it works. Again, always remember that adding an element to this set does not change the set. It produces a new set that has the added item.
OK, now that we've got that, let's consider a more efficient algorithm for producing your permutations.
We will solve the problem recursively. A recursive solution always has the same structure:
Can we solve a trivial problem? If so, solve it.
If not, break the problem down into a number of smaller problems and solve each one.
Let's start with the trivial problems.
Suppose you have a set and you wish to choose zero items from it. The answer is clear: there is only one possible permutation with zero elements, and that is the empty set.
Suppose you have a set with n elements in it and you want to choose more than n elements. Clearly there is no solution, not even the empty set.
We have now taken care of the cases where the set is empty or the number of elements chosen is more than the number of elements total, so we must be choosing at least one thing from a set that has at least one thing.
Of the possible permutations, some of them have the first element in them and some of them do not. Find all the ones that have the first element in them and yield them. We do this by recursing to choose one fewer elements on the set that is missing the first element.
The ones that do not have the first element in them we find by enumerating the permutations of the set without the first element.
static class Extensions
{
public static IEnumerable<BitSet> Choose(this BitSet b, int choose)
{
if (choose < 0) throw new InvalidOperationException();
if (choose == 0)
{
// Choosing zero elements from any set gives the empty set.
yield return BitSet.Empty;
}
else if (b.Count() >= choose)
{
// We are choosing at least one element from a set that has
// a first element. Get the first element, and the set
// lacking the first element.
int first = b.First();
BitSet rest = b.Remove(first);
// These are the permutations that contain the first element:
foreach(BitSet r in rest.Choose(choose-1))
yield return r.Add(first);
// These are the permutations that do not contain the first element:
foreach(BitSet r in rest.Choose(choose))
yield return r;
}
}
}
Now we can ask the question that you need the answer to:
class Program
{
static void Main()
{
BitSet b = BitSet.Empty.Add(1).Add(2).Add(3).Add(4).Add(5).Add(6).Add(7);
foreach(BitSet result in b.Choose(3))
Console.WriteLine(result);
}
}
And we're done. We have generated only as many sequences as we actually need. (Though we have done a lot of set operations to get there, but set operations are cheap.) The point here is that understanding how this algorithm works is extremely instructive. Recursive programming on immutable structures is a powerful tool that many professional programmers do not have in their toolbox.
You can do it like this:
var data = Enumerable.Range(1, 7);
var r = from a in data
from b in data
from c in data
where a < b && b < c
select new {a, b, c};
foreach (var x in r) {
Console.WriteLine("{0} {1} {2}", x.a, x.b, x.c);
}
Demo.
Edit: Thanks Eric Lippert for simplifying the answer!
var ints = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var permutations = ints.SelectMany(a => ints.Where(b => (b > a)).
SelectMany(b => ints.Where(c => (c > b)).
Select(c => new { a = a, b = b, c = c })));

Repeat numbers in List/IEnumerable

I have a list, e.g.
List<int> List1 = new List<int>{1, 5, 8, 3, 9};
What is a simple way of repeating the elements in the list to obtain {1, 1, 5, 5, 8, 8, 3, 3, 9, 9}?
The reason I need this is that I am plotting the elements in the list and need to make a "step plot".
var list2 = List1.SelectMany(x => new []{x, x}).ToList();
I would create (extension) method which enumerates source and yields each item required number of times:
public static IEnumerable<T> RepeatItems<T>(this IEnumeable<T> source, int count)
{
foreach(var item in source)
for(int i = 0; i < count; i++)
yield return item;
}
Thus you will avoid creating huge number of arrays. Usage:
var result = List1.RepeatItems(2).ToList();
If you need just to duplicate items, then solution is even more simple:
public static IEnumerable<T> DuplicateItems<T>(this IEnumeable<T> source)
{
foreach(var item in source)
{
yield return item;
yield return item;
}
}
Usage of DuplicateItems extension:
var result = List1.DuplicateItems().ToList();
Also if you will only enumerate result, then you don't need to convert it to list. If you will not modify (add/remove) items from result, then converting it to array is more efficient.
Taken from the comments above,
var sequence2 = List1.SelectMany(x => Enumerable.Repeat(x, 2));
is a better solution becuase it avoids pointless allocation of memory. It would also be simpler to change to n repetitions where the variation in overhead would become more significant.
It's you're trying to reduce memory allocations:
// Pre-allocate the space to save time
List<int> dups = new List(List1.Count * 2);
// Avoid allocating an enumerator (hopefully!)
for(int i=0; i<List1.Count; i++)
{
var value = List1[i];
dups.Add(value);
dups.Add(value);
}
It's not Linq, but it's memory efficient

Is there a C# equivalent to C++ std::partial_sort?

I'm trying to implement a paging algorithm for a dataset sortable via many criteria. Unfortunately, while some of those criteria can be implemented at the database level, some must be done at the app level (we have to integrate with another data source). We have a paging (actually infinite scroll) requirement and are looking for a way to minimize the pain of sorting the entire dataset at the app level with every paging call.
What is the best way to do a partial sort, only sorting the part of the list that absolutely needs to be sorted? Is there an equivalent to C++'s std::partial_sort function available in the .NET libraries? How should I go about solving this problem?
EDIT: Here's an example of what I'm going for:
Let's say I need to get elements 21-40 of a 1000 element set, according to some sorting criteria. In order to speed up the sort, and since I have to go through the whole dataset every time anyway (this is a web service over HTTP, which is stateless), I don't need the whole dataset ordered. I only need elements 21-40 to be correctly ordered. It is sufficient to create 3 partitions: Elements 1-20, unsorted (but all less than element 21); elements 21-40, sorted; and elements 41-1000, unsorted (but all greater than element 40).
OK. Here's what I would try based on what you said in reply to my comment.
I want to be able to say "4th through 6th" and get something like: 3,
2, 1 (unsorted, but all less than proper 4th element); 4, 5, 6 (sorted
and in the same place they would be for a sorted list); 8, 7, 9
(unsorted, but all greater than proper 6th element).
Lets add 10 to our list to make it easier: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1.
So, what you could do is use the quick select algorithm to find the the ith and kth elements. In your case above i is 4 and k is 6. That will of course return the values 4 and 6. That's going to take two passes through your list. So, so far the runtime is O(2n) = O(n). The next part is easy, of course. We have lower and upper bounds on the data we care about. All we need to do is make another pass through our list looking for any element that is between our upper and lower bounds. If we find such an element we throw it into a new List. Finally, we then sort our List which contains only the ith through kth elements that we care about.
So, I believe the total runtime ends up being O(N) + O((k-i)lg(k-i))
static void Main(string[] args) {
//create an array of 10 million items that are randomly ordered
var list = Enumerable.Range(1, 10000000).OrderBy(x => Guid.NewGuid()).ToList();
var sw = Stopwatch.StartNew();
var slowOrder = list.OrderBy(x => x).Skip(10).Take(10).ToList();
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
//Took ~8 seconds on my machine
sw.Restart();
var smallVal = Quickselect(list, 11);
var largeVal = Quickselect(list, 20);
var elements = list.Where(el => el >= smallVal && el <= largeVal).OrderBy(el => el);
Console.WriteLine(sw.ElapsedMilliseconds);
//Took ~1 second on my machine
}
public static T Quickselect<T>(IList<T> list , int k) where T : IComparable {
Random rand = new Random();
int r = rand.Next(0, list.Count);
T pivot = list[r];
List<T> smaller = new List<T>();
List<T> larger = new List<T>();
foreach (T element in list) {
var comparison = element.CompareTo(pivot);
if (comparison == -1) {
smaller.Add(element);
}
else if (comparison == 1) {
larger.Add(element);
}
}
if (k <= smaller.Count) {
return Quickselect(smaller, k);
}
else if (k > list.Count - larger.Count) {
return Quickselect(larger, k - (list.Count - larger.Count));
}
else {
return pivot;
}
}
You can use List<T>.Sort(int, int, IComparer<T>):
inputList.Sort(startIndex, count, Comparer<T>.Default);
Array.Sort() has an overload that accepts index and length arguments that lets you sort a subset of an array. The same exists for List.
You cannot sort an IEnumerable directly, of course.

IComparable<T> gives stackoverflow when used for negative numbers?

This is a weired problem, I have implemented simple quick sort as follows..
static void Main(string[] args)
{
List<int> unsorted = new List<int> { 1, 3, 5, 7, 9, 8, 6, 4, 2 };
List<int> sorted = quicksort(unsorted);
Console.WriteLine(string.Join(",", sorted));
Console.ReadKey();
}
private static List<T> quicksort<T>(List<T> arr) where T : IComparable<T>
{
List<T> loe = new List<T>(), gt = new List<T>();
if (arr.Count < 2)
return arr;
int pivot = arr.Count / 2;
T pivot_val = arr[pivot];
arr.RemoveAt(pivot);
foreach (T i in arr)
{
if (i.CompareTo(pivot_val) <= 0)
loe.Add(i);
else
gt.Add(i);
}
List<T> resultSet = new List<T>();
resultSet.AddRange(quicksort(loe));
gt.Add(pivot_val);
resultSet.AddRange(quicksort(gt));
return resultSet;
}
Output is : 1,2,3,4,5,6,7,8,9
But When I use any negative number in the unsorted list there is a stackoverflow error,
for example
if List unsorted = new List { 1, 3, 5, 7, 9, 8, 6, 4, 2, -1 };
Now there is a stackoverflow..
What's going on? Why this is not working ?
Your algorithm has a bug. Consider the simplest input list { 1, -1 }. Let's step through your logic.
You first choose a pivot index, Count / 2, which is 1.
You then remove the pivot element at index 1 (-1) from the arr list.
Next you compare each remaining element in the arr list (there's just the 1 at index 0) with the pivot.
The 1 is greater than the pivot (-1) so you add it to the gt list.
Next you quicksort the loe list, which is empty. That sort returns an empty list, which you add to the result set.
You then add the pivot value to the end of the gt list, so the gt list now looks like this: { 1, -1 }. Notice that this is the exact same list as you started with.
You then attempt to quicksort the gt list. Since you are calling the quicksort routine with the same input, the same sequence of steps happens again, until the stack overflows.
It seems the error in your logic is that you blindly add the pivot to the gt list without comparing it to anything. I'll leave it to you to figure out how to make it work.
Edited to add: I'm assuming this is a homework assignment, but if it's not, I would highly recommend using .NET's built in Sort() method on List<T>. It has been highly optimized and heavily tested, and will most likely perform better than anything home-brewed. Why reinvent the wheel?
if you don't have a debugger try this...
foreach (T i in arr)
{
if (i.CompareTo(pivot_val) <= 0)
{
loe.Add(i);
Console.WriteLine("loe.add " + i.ToString());
}
else
{
gt.Add(i);
Console.WriteLine("gt.add " + i.ToString());
}
}

Categories

Resources