Combine TakeWhile and SkipWhile to partition collection - c#

I would like to partition collection on item, which matches specific condition. I can do that using TakeWhile and SkipWhile, which is pretty easy to understand:
public static bool IsNotSeparator(int value) => value != 3;
var collection = new [] { 1, 2, 3, 4, 5 };
var part1 = collection.TakeWhile(IsNotSeparator);
var part2 = collection.SkipWhile(IsNotSeparator);
But this would iterate from start of collection twice and if IsNotSeparator takes long it might be performance issue.
Faster way would be to use something like:
var part1 = new List<int>();
var index = 0;
for (var max = collection.Length; index < max; ++index) {
if (IsNotSeparator(collection[i]))
part1.Add(collection[i]);
else
break;
}
var part2 = collection.Skip(index);
But that's really less more readable than first example.
So my question is: what would be the best solution to partition collection on specific element?
What I though of combining those two above is:
var collection = new [] { 1, 2, 3, 4, 5 };
var part1 = collection.TakeWhile(IsNotSeparator).ToList();
var part2 = collection.Skip(part1.Count);

This is a quick example of how you would do the more general method (multiple splits, as mentioned in the comments), without LINQ (it's possible to convert it to LINQ, but I am not sure if it will be any more readable, and I am in a slight hurry right now):
public static IEnumerable<IEnumerable<T>> Split<T>(this IList<T> list, Predicate<T> match)
{
if (list.Count == 0)
yield break;
var chunkStart = 0;
for (int i = 1; i < list.Count; i++)
{
if (match(list[i]))
{
yield return new ListSegment<T>(list, chunkStart, i - 1);
chunkStart = i;
}
}
yield return new ListSegment<T>(list, chunkStart, list.Count - 1);
}
The code presumes a class named ListSegment<T> : IEnumerable<T> which simply iterates from from to to over the original list (no copying, similar to how ArraySegment<T> works (but is unfortunately limited to arrays).
So the code will return as many chunks as there are matches, i.e. this code:
var collection = new[] { "A", "B", "-", "C", "D", "-", "E" };
foreach (var chunk in collection.Split(i => i == "-"))
Console.WriteLine(string.Join(", ", chunk));
would print:
A, B
-, C, D
-, E

How about using the Array Copy methods:
var separator = 3;
var collection = new [] { 1, 2, 3, 4, 5 };
var i = Array.IndexOf(collection,separator);
int[] part1 = new int[i];
int[] part2 = new int[collection.Length - i];
Array.Copy(collection, 0, part1, 0, i );
Array.Copy(collection, i, part2, 0, collection.Length - i );
Alternatively to be more efficient use ArraySegment:
var i = Array.IndexOf(collection,separator);
var part1 = new ArraySegment<int>( collection, 0, i );
var part2 = new ArraySegment<int>( collection, i, collection.Length - i );
ArraySegment is a wrapper around an array that delimits a range of elements in that array. Multiple ArraySegment instances can refer to the same original array and can overlap.
Edit - add combination of original question with ArraySegment so as not to iterate collection twice.
public static bool IsNotSeparator(int value) => value != 3;
var collection = new [] { 1, 2, 3, 4, 5 };
var index = collection.TakeWhile(IsNotSeparator).Count();
var part1 = new ArraySegment<int>( collection, 0, index );
var part2 = new ArraySegment<int>( collection, index, collection.Length - index );

Related

Split list by element

I have list of 1 and 0 like this:
var list = new List<int>{1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1}
between two items, can be only one zero.
How to split that list into sublists by 0?
Other words: if I have string like this: string myString = "111011011110111111011101" then it is easy to split it by 0 into few strings.
But how to do it with list? This example shoudl produce these sublists:
1,1,1
1,1
1,1,1,1
1,1,1,1,1,1
1,1,1
1
so is there better way then casting each element into string, joining them and doing what I show what can be done with string ?
You can solve your problem by transforming the input sequence into a sequence of sequences just like the LINQ GroupBy does. However, in your case you are grouping on a change in the input sequence. There is perhaps the possibility of combining existing LINQ operators like GroupBy, Zip and Skip into something that does what you want but I think it is easier (and performs better) to create an iterator block that looks at pairs of items in the input sequence:
static class EnumerableExtensions {
public static IEnumerable<IEnumerable<T>> GroupOnChange<T>(
this IEnumerable<T> source,
Func<T, T, Boolean> changePredicate
) {
if (source == null)
throw new ArgumentNullException("source");
if (changePredicate == null)
throw new ArgumentNullException("changePredicate");
using (var enumerator = source.GetEnumerator()) {
if (!enumerator.MoveNext())
yield break;
var firstValue = enumerator.Current;
var currentGroup = new List<T>();
currentGroup.Add(firstValue);
while (enumerator.MoveNext()) {
var secondValue = enumerator.Current;
var change = changePredicate(firstValue, secondValue);
if (change) {
yield return currentGroup;
currentGroup = new List<T>();
}
currentGroup.Add(secondValue);
firstValue = secondValue;
}
yield return currentGroup;
}
}
}
GroupOnChange will take the items in the input sequence and group them into a sequence of sequences. A new group is started when changePredicate is true.
You can use GroupOnChange to split your input sequence exactly as you want to. You then have to remove the groups that have zero as a value by using Where.
var groups = items
.GroupOnChange((first, second) => first != second)
.Where(group => group.First() != 0);
You can also use this approach if the input are class instances and you want to group by a property of that class. You then have to modify the predicate accordingly to compare the properties. (I know you need this because you asked a now deleted question that was slightly more complicated where the input sequence was not simply numbers but classes with a number property.)
You could write an extension method like this:
public static class Extensions
{
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(this IEnumerable<TSource> source, TSource splitOn, IEqualityComparer<TSource> comparer = null)
{
if (source == null)
throw new ArgumentNullException("source");
return SplitIterator(source, splitOn, comparer);
}
private static IEnumerable<IEnumerable<TSource>> SplitIterator<TSource>(this IEnumerable<TSource> source, TSource splitOn, IEqualityComparer<TSource> comparer)
{
comparer = comparer ?? EqualityComparer<TSource>.Default;
var current = new List<TSource>();
foreach (var item in source)
{
if (comparer.Equals(item, splitOn))
{
if (current.Count > 0)
{
yield return current;
current = new List<TSource>();
}
}
else
{
current.Add(item);
}
}
if (current.Count > 0)
yield return current;
}
}
And use it like this:
var list = new List<int>{1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1};
var result = list.Split(0);
int c = 0;
var list = new List<int>{1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1};
var res = list
// split in groups and set their numbers
// c is a captured variable
.Select(x=>new {Item = x, Subgroup = x==1 ? c : c++})
// remove zeros
.Where(x=>x.Item!=0)
// create groups
.GroupBy(x=>x.Subgroup)
// convert to format List<List<int>>
.Select(gr=>gr.Select(w=>w.Item).ToList())
.ToList();
You can just group by the index of the next zero:
static void Main(string[] args)
{
var list = new List<int> { 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 };
var result = list.Select((e, i) => new { Element = e, Index = i })
.Where(e => e.Element == 1)
.GroupBy(e => list.IndexOf(0, e.Index));
}
Maybe something simpler:
IEnumerable<IEnumerable<int>> Split(IEnumerable<int> items)
{
List<int> result = new List<int>();
foreach (int item in items)
if (item == 0 && result.Any())
{
yield return result;
result = new List<int>();
}
else
result.Add(item);
if (result.Any())
yield return result;
}
The code below splits by iterating over the list and storing sub-sequences of '1' in a list of lists.
using System;
using System.Collections.Generic;
namespace SplitList
{
class Program
{
static void Main(string[] args)
{
var list = new List<int> { 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 };
List<List<int>> splitSequences = new List<List<int>>();
List<int> curSequence = new List<int>();
foreach (var item in list)
{
if (item == 1) {
curSequence.Add(item);
} else {
//Only push the current sequence onto the list of sequences if it is not empty,
//which could happen if multiple zeroes are encountered in a row
if (curSequence.Count > 0) {
splitSequences.Add(curSequence);
curSequence = new List<int>();
}
}
}
//push any final list
if (curSequence.Count > 0)
{
splitSequences.Add(curSequence);
}
foreach (var seq in splitSequences) {
String line = String.Join(",", seq);
Console.WriteLine(line);
}
Console.WriteLine("");
Console.WriteLine("Press any key to exit");
var discard = Console.ReadKey();
}
}
}
I did like ASh's solution most. I've used it with a slight change. There is no captured variable in "my" variant:
var list = new List<int> { 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 };
var res = list
// Step 1: associate every value with an index
.Select((x, i) => (Value: x, Index: i))
// Step 2: remove separator values
.Where(x => x.Value != 0)
// Step 3: adjacent items will have the same result
// subtracting real position from the index marked during the 1st step
.Select((tuple, realIndex) => (tuple.Value, GroupId: tuple.Index - realIndex))
// Step 4: group by groupId
.GroupBy(tuple => tuple.GroupId)
// Step 5: convert to List<List<int>>
.Select(group => group.Select(tuple => tuple.Value).ToList())
.ToList();
More on step 3:
Let's say, we have 5 items with indices:
[ 0 1 2 3 4 ]
{ 1, 1, 0, 1, 1 }
After filtration from the step 2, list of numbers looks next:
[ 0 1 3 4 ]
{ 1, 1, 1, 1 }
What does step 3:
real indices (ri): [ 0 1 2 3 ]
indices from the 1st step (i): [ 0 1 3 4 ]
numbers: { 1, 1, 1, 1 }
i - ri: [ 0, 0, 1, 1 ]
And step 4 just groups by result of the subtraction i - ri, so called GroupID.

Splitting an array using LINQ

I have a collection uni-dimensional like this:
[1,2,4,5.....n]
I would like to convert that collection in a bi-dimensional collection like this:
[[1,2,3],
[4,5,6],
...]
Basically I want to group or split if you want, the array in groups of 'n' members
I can do it with a foreach statement, but I am currently learning LINQ so instead of iterating through all elements and create a new array manually I would like to use the LINQ features (if applicable)
Is there any LINQ function to help me to accomplish this??
I was thinking in the GroupBy or SelectMany I do not know if they will help me though but they might
Any help will be truly appreciate it =) :**
You can group by the index divided by the batch size, like this:
var batchSize = 3;
var batched = orig
.Select((Value, Index) => new {Value, Index})
.GroupBy(p => p.Index/batchSize)
.Select(g => g.Select(p => p.Value).ToList());
Use MoreLinq.Batch
var result = inputArray.Batch(n); // n -> batch size
Example
var inputs = Enumerable.Range(1,10);
var output = inputs.Batch(3);
var outputAsArray = inputs.Batch(3).Select(x=>x.ToArray()).ToArray(); //If require as array
You want Take() and Skip(). These methods will let you split an IEnumerable. Then you can use Concat() to slap them together again.
The sample below will split an array into groups of 4 items each.
int[] items = Enumerable.Range(1, 20).ToArray(); // Generate a test array to split
int[][] groupedItems = items
.Select((item, index) => index % 4 == 0 ? items.Skip(index).Take(4).ToArray() : null)
.Where(group => group != null)
.ToArray();
It's not a pure LINQ but it's intended to be used with it:
public static class MyEnumerableExtensions
{
public static IEnumerable<T[]> Split<T>(this IEnumerable<T> source, int size)
{
if (source == null)
{
throw new ArgumentNullException("source can't be null.");
}
if (size == 0)
{
throw new ArgumentOutOfRangeException("Chunk size can't be 0.");
}
List<T> result = new List<T>(size);
foreach (T x in source)
{
result.Add(x);
if (result.Count == size)
{
yield return result.ToArray();
result = new List<T>(size);
}
}
}
}
It can be used from your code as:
private void Test()
{
// Here's your original sequence
IEnumerable<int> seq = new[] { 1, 2, 3, 4, 5, 6 };
// Here's the result of splitting into chunks of some length
// (here's the chunks length equals 3).
// You can manipulate with this sequence further,
// like filtering or joining e.t.c.
var splitted = seq.Split(3);
}
It's as simple as:
static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> ToPages<T>(this IEnumerable<T> elements, int pageSize)
{
if (elements == null)
throw new ArgumentNullException("elements");
if (pageSize <= 0)
throw new ArgumentOutOfRangeException("pageSize","Must be greater than 0!");
int i = 0;
var paged = elements.GroupBy(p => i++ / pageSize);
return paged;
}
}
I based my solution of Jeremy Holovacs's answer and used Take() and Skip() to create subarrays.
const int batchSize = 3;
int[] array = new int[] { 1,2,4,5.....n};
var subArrays = from index in Enumerable.Range(0, array.Length / batchSize + 1)
select array.Skip(index * batchSize).Take(batchSize);
Starting with .NET 6, there is the System.Linq.Enumerable.Chunk(this IEnumerable<TSource>, int size) extension method. It returns an IEnumerable<TSource[]> where each item is an array of size elements, except the last item, which could have fewer.
Code like this:
using System;
using System.Collections.Generic;
using System.Linq;
int[] input = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<int[]> chunks = input.Chunk(3);
foreach (int[] chunk in chunks)
{
foreach (int i in chunk)
{
Console.Write($"{i} ");
}
Console.WriteLine();
}
outputs
1 2 3
4 5 6
7 8 9
10

C#: Altering values for every item in an array

I'm wondering if there is built-in .NET functionality to change each value in an array based on the result of a provided delegate. For example, if I had an array {1,2,3} and a delegate that returns the square of each value, I would like to be able to run a method that takes the array and delegate, and returns {1,4,9}. Does anything like this exist already?
LINQ provides support for projections using the Select extension method:
var numbers = new[] {1, 2, 3};
var squares = numbers.Select(i => i*i).ToArray();
You can also use the slightly less fluent Array.ConvertAll method:
var squares = Array.ConvertAll(numbers, i => i*i);
Jagged arrays can be processed by nesting the projections:
var numbers = new[] {new[] {1, 2}, new[] {3, 4}};
var squares = numbers.Select(i => i.Select(j => j*j).ToArray()).ToArray();
Multidimensional arrays are a little more complex. I've written the following extension method which projects every element in a multidimensional array no matter what its rank.
static Array ConvertAll<TSource, TResult>(this Array source,
Converter<TSource, TResult> projection)
{
if (!typeof (TSource).IsAssignableFrom(source.GetType().GetElementType()))
{
throw new ArgumentException();
}
var dims = Enumerable.Range(0, source.Rank)
.Select(dim => new {lower = source.GetLowerBound(dim),
upper = source.GetUpperBound(dim)});
var result = Array.CreateInstance(typeof (TResult),
dims.Select(dim => 1 + dim.upper - dim.lower).ToArray(),
dims.Select(dim => dim.lower).ToArray());
var indices = dims
.Select(dim => Enumerable.Range(dim.lower, 1 + dim.upper - dim.lower))
.Aggregate(
(IEnumerable<IEnumerable<int>>) null,
(total, current) => total != null
? total.SelectMany(
item => current,
(existing, item) => existing.Concat(new[] {item}))
: current.Select(item => (IEnumerable<int>) new[] {item}))
.Select(index => index.ToArray());
foreach (var index in indices)
{
var value = (TSource) source.GetValue(index);
result.SetValue(projection(value), index);
}
return result;
}
The above method can be tested with an array of rank 3 as follows:
var source = new int[2,3,4];
for (var i = source.GetLowerBound(0); i <= source.GetUpperBound(0); i++)
for (var j = source.GetLowerBound(1); j <= source.GetUpperBound(1); j++)
for (var k = source.GetLowerBound(2); k <= source.GetUpperBound(2); k++)
source[i, j, k] = i*100 + j*10 + k;
var result = (int[,,]) source.ConvertAll<int, int>(i => i*i);
for (var i = source.GetLowerBound(0); i <= source.GetUpperBound(0); i++)
for (var j = source.GetLowerBound(1); j <= source.GetUpperBound(1); j++)
for (var k = source.GetLowerBound(2); k <= source.GetUpperBound(2); k++)
{
var value = source[i, j, k];
Debug.Assert(result[i, j, k] == value*value);
}
Not that I'm aware of (replacing each element rather than converting to a new array or sequence), but it's incredibly easy to write:
public static void ConvertInPlace<T>(this IList<T> source, Func<T, T> projection)
{
for (int i = 0; i < source.Count; i++)
{
source[i] = projection(source[i]);
}
}
Use:
int[] values = { 1, 2, 3 };
values.ConvertInPlace(x => x * x);
Of course if you don't really need to change the existing array, the other answers posted using Select would be more functional. Or the existing ConvertAll method from .NET 2:
int[] values = { 1, 2, 3 };
values = Array.ConvertAll(values, x => x * x);
This is all assuming a single-dimensional array. If you want to include rectangular arrays, it gets trickier, particularly if you want to avoid boxing.
Using System.Linq you could do something like:
var newArray = arr.Select(x => myMethod(x)).ToArray();
LINQ queries could easily solve this for you - make sure you're referencing System.Core.dll and have a
using System.Linq;
statement. For example, if you had your array in a variable named numberArray, the following code would give you exactly what you're looking for:
var squares = numberArray.Select(n => n * n).ToArray();
The final "ToArray" call is only needed if you actually need an array, and not an IEnumerable<int>.
you can use linq to accomplish this in shorthand but be careful remember that a foreach occurs underneath anyway.
int[] x = {1,2,3};
x = x.Select(( Y ) => { return Y * Y; }).ToArray();
Here is another solution for M x N arrays, where M and N are not known at compile time.
// credit: https://blogs.msdn.microsoft.com/ericlippert/2010/06/28/computing-a-cartesian-product-with-linq/
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };
foreach (var sequence in sequences)
{
// got a warning about different compiler behavior
// accessing sequence in a closure
var s = sequence;
result = result.SelectMany(seq => s, (seq, item) => seq.Concat<T>(new[] { item }));
}
return result;
}
public static void ConvertInPlace(this Array array, Func<object, object> projection)
{
if (array == null)
{
return;
}
// build up the range for each dimension
var dimensions = Enumerable.Range(0, array.Rank).Select(r => Enumerable.Range(0, array.GetLength(r)));
// build up a list of all possible indices
var indexes = EnumerableHelper.CartesianProduct(dimensions).ToArray();
foreach (var index in indexes)
{
var currentIndex = index.ToArray();
array.SetValue(projection(array.GetValue(currentIndex)), currentIndex);
}
}

how to split list using linq

i have List of int which consists of value 0,0,0,1,2,3,4,0,0 now i like to split this into 3 lists like this list A consists 0,0,0 and List B consists 1,2,3,4 and List C consists 0,0.I know how split using if and for,but how can i do this using linq. usual format i need split in starting some zeros and in middle some values and in last some zeros i need to split this first zeros in one list ,middle values in one list and end zeros in another list as i say in example above here using linq and also i like to take the index that values.
first one.
myList.TakeWhile(x => x==0)
second one.
myList.SkipWhile(x => x==0).TakeWhile(x => x!= 0)
third one.
myList.SkipWhile(x => x==0).SkipWhile(x => x!= 0)
If you want to split by zero sequence then try this code:
static void Main(string[] argv)
{
var list = new[] { 0, 0, 0, 1, 2, 3, 4, 0, 0 };
int groupIndex = 0;
var result = list.Select(
(e, i) =>
{
if (i == 0)
{
return new {val = e, group = groupIndex};
}
else
{
groupIndex =
(e != 0 && list[i - 1] == 0) || (e == 0 && list[i - 1] != 0)
?
groupIndex + 1
: groupIndex;
return new {val = e, group = groupIndex};
}
}
).GroupBy(e => e.group).Select(e => e.Select(o => o.val).ToList()).ToList();
foreach (var item in result)
{
foreach (var val in item)
{
Console.Write(val + ";");
}
Console.WriteLine();
Console.WriteLine("Count:" + item.Count);
Console.WriteLine();
}
Console.ReadLine();
}
Output is:
0;0;0;
Count:3
1;2;3;4;
Count:4
0;0;
Count:2
It is really not clear what is a criteria of split from your question. If I gave wrong answer then explain your question.
You can use the Skip and Take methods exposed by Linq to Objects to grab certain elements of a sequence.
var myList = new int[] {0,0,0,1,2,3,4,0,0};
var list1 = myList.Take(3);
var list2 = myList.Skip(3).Take(4);
var list3 = myList.Skip(7);
You can use Take(n) or Skip(n) in linq
List<int> list = new List<int>();
list.Add(0);
list.Add(0);
list.Add(0);
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(0);
list.Add(0);
var listOne = list.Take(3);
var listSecond = list.Skip(3).Take(4);
var listThird = list.Skip(7);

Selecting unique elements from a List in C#

How do I select the unique elements from the list {0, 1, 2, 2, 2, 3, 4, 4, 5} so that I get {0, 1, 3, 5}, effectively removing all instances of the repeated elements {2, 4}?
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };
var uniqueNumbers =
from n in numbers
group n by n into nGroup
where nGroup.Count() == 1
select nGroup.Key;
// { 0, 1, 3, 5 }
var nums = new int{ 0...4,4,5};
var distinct = nums.Distinct();
make sure you're using Linq and .NET framework 3.5.
With lambda..
var all = new[] {0,1,1,2,3,4,4,4,5,6,7,8,8}.ToList();
var unique = all.GroupBy(i => i).Where(i => i.Count() == 1).Select(i=>i.Key);
C# 2.0 solution:
static IEnumerable<T> GetUniques<T>(IEnumerable<T> things)
{
Dictionary<T, int> counts = new Dictionary<T, int>();
foreach (T item in things)
{
int count;
if (counts.TryGetValue(item, out count))
counts[item] = ++count;
else
counts.Add(item, 1);
}
foreach (KeyValuePair<T, int> kvp in counts)
{
if (kvp.Value == 1)
yield return kvp.Key;
}
}
Here is another way that works if you have complex type objects in your List and want to get the unique values of a property:
var uniqueValues= myItems.Select(k => k.MyProperty)
.GroupBy(g => g)
.Where(c => c.Count() == 1)
.Select(k => k.Key)
.ToList();
Or to get distinct values:
var distinctValues = myItems.Select(p => p.MyProperty)
.Distinct()
.ToList();
If your property is also a complex type you can create a custom comparer for the Distinct(), such as Distinct(OrderComparer), where OrderComparer could look like:
public class OrderComparer : IEqualityComparer<Order>
{
public bool Equals(Order o1, Order o2)
{
return o1.OrderID == o2.OrderID;
}
public int GetHashCode(Order obj)
{
return obj.OrderID.GetHashCode();
}
}
If Linq isn't available to you because you have to support legacy code that can't be upgraded, then declare a Dictionary, where the first int is the number and the second int is the number of occurences. Loop through your List, loading up your Dictionary. When you're done, loop through your Dictionary selecting only those elements where the number of occurences is 1.
I believe Matt meant to say:
static IEnumerable<T> GetUniques<T>(IEnumerable<T> things)
{
Dictionary<T, bool> uniques = new Dictionary<T, bool>();
foreach (T item in things)
{
if (!(uniques.ContainsKey(item)))
{
uniques.Add(item, true);
}
}
return uniques.Keys;
}
There are many ways to skin a cat, but HashSet seems made for the task here.
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };
HashSet<int> r = new HashSet<int>(numbers);
foreach( int i in r ) {
Console.Write( "{0} ", i );
}
The output:
0 1 2 3 4 5
Here's a solution with no LINQ:
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };
// This assumes the numbers are sorted
var noRepeats = new List<int>();
int temp = numbers[0]; // Or .First() if using IEnumerable
var count = 1;
for(int i = 1; i < numbers.Length; i++) // Or foreach (var n in numbers.Skip(1)) if using IEnumerable
{
if (numbers[i] == temp) count++;
else
{
if(count == 1) noRepeats.Add(temp);
temp = numbers[i];
count = 1;
}
}
if(count == 1) noRepeats.Add(temp);
Console.WriteLine($"[{string.Join(separator: ",", values: numbers)}] -> [{string.Join(separator: ",", values: noRepeats)}]");
This prints:
[0,1,2,2,2,3,4,4,5] -> [0,1,3,5]
In .Net 2.0 I`m pretty sure about this solution:
public IEnumerable<T> Distinct<T>(IEnumerable<T> source)
{
List<T> uniques = new List<T>();
foreach (T item in source)
{
if (!uniques.Contains(item)) uniques.Add(item);
}
return uniques;
}

Categories