A dictionary object that uses ranges of values for keys - c#

I have need of a sort of specialized dictionary. My use case is this: The user wants to specify ranges of values (the range could be a single point as well) and assign a value to a particular range. We then want to perform a lookup using a single value as a key. If this single value occurs within one of the ranges then we will return the value associated to the range.
For example:
// represents the keyed value
struct Interval
{
public int Min;
public int Max;
}
// some code elsewhere in the program
var dictionary = new Dictionary<Interval, double>();
dictionary.Add(new Interval { Min = 0, Max = 10 }, 9.0);
var result = dictionary[1];
if (result == 9.0) JumpForJoy();
This is obviously just some code to illustrate what I'm looking for. Does anyone know of an algorithm to implement such a thing? If so could they point me towards it, please?
I have already tried implementing a custom IEqualityComparer object and overloading Equals() and GetHashCode() on Interval but to no avail so far. It may be that I'm doing something wrong though.

A dictionary is not the appropriate data structure for the operations you are describing.
If the intervals are required to never overlap then you can just build a sorted list of intervals and binary search it.
If the intervals can overlap then you have a more difficult problem to solve. To solve that problem efficiently you'll want to build an interval tree:
http://en.wikipedia.org/wiki/Interval_tree
This is a well-known data structure. See "Introduction To Algorithms" or any other decent undergraduate text on data structures.

This is only going to work when the intervals don't overlap. And your main problem seems to be converting from a single (key) value to an interval.
I would write a wrapper around SortedList. The SortedList.Keys.IndexOf() would find you an index that can be used to verify if the interval is valid and then use it.

This isn't exactly what you want but I think it may be the closest you can expect.
You can of course do better than this (Was I drinking earlier?). But you have to admit it is nice and simple.
var map = new Dictionary<Func<double, bool>, double>()
{
{ d => d >= 0.0 && d <= 10.0, 9.0 }
};
var key = map.Keys.Single(test => test(1.0))
var value = map[key];

I have solved a similar problem by ensuring that the collection is contiguous where the intervals never overlap and never have gaps between them. Each interval is defined as a lower boundary and any value lies in that interval if it is equal to or greater than that boundary and less than the lower boundary of the next interval. Anything below the lowest boundary is a special case bin.
This simplifies the problem somewhat. We also then optimized key searches by implementing a binary chop. I can't share the code, unfortunately.

I would make a little Interval class, which would something like that:
public class Interval
{
public int Start {get; set;}
public int End {get; set;}
public int Step {get; set;}
public double Value {get; set;}
public WriteToDictionary(Dictionary<int, double> dict)
{
for(int i = Start; i < End; i += Step)
{
dict.Add(i, Value);
}
}
}
So you still can a normal lookup within your dictionary. Maybe you should also perform some checks before calling Add() or implement some kind of rollback if any value is already within the dictionary.

You can find a Java flavored C# implementation of an interval tree in the Open Geospatial Library. It needs some minor tweaks to solve your problem and it could also really use some C#-ification.
It's Open Source but I don't know under what license.

i adapted some ideas for Dictionary and func, like "ChaosPandion" gave me the idea in his earlier post here above.
i still solved the coding, but if i try to refactor
i have a amazing problem/bug/lack of understanding:
Dictionary<Func<string, double, bool>, double> map = new Dictionary<Func<string, double, bool>, double>()
{
{ (a, b) => a == "2018" && b == 4, 815.72},
{ (a, b) => a == "2018" && b == 6, 715.72}
};
What is does is, that i call the map with a search like "2018"(year) and 4(month), which the result is double value 815,72.
When i check the unique map entries they look like this:
map working unique keys
so thats the orginal behaviour, anything fine so far.
Then i try to refactor it, to this:
Dictionary<Func<string, double, bool>, double> map =
new Dictionary<Func<string, double, bool>, double>();
WS22(map, values2018, "2018");
private void WS22(Dictionary<Func<string, double, bool>, double> map, double[] valuesByYear, string strYear)
{
int iMonth = 1;
// step by step this works:
map.Add((a, b) => (a == strYear) && (b == 1), dValue);
map.Add((a, b) => (a == strYear) && (b == 2), dValue);
// do it more elegant...
foreach (double dValue in valuesByYear)
{
//this does not work: exception after second iteration of foreach run
map.Add((a, b) => (a == strYear) && (b == iMonth), dValue );
iMonth+=1;
}
}
this works: (i use b==1 and b==2)
this does not work (map not working exception on add item on second iteration)
so i think the problem is, that the map does not have a unique key while adding to map dictionary. The thing is, i dont see my error, why b==1 is working and b==iMonth not.
Thx for any help, that open my eyes :)

Using Binary Search, I created an MSTest v2 test case that approaches the solution. It assumes that the index is the actual number you are looking for, which does not (might not?) suit the description given by the OP.
Note that the ranges do not overlap. And that the ranges are
[negative infinity, 0)
[0, 5]
(5, 15]
(15, 30]
(30, 100]
(100, 500]
(500, positive infinity]
This values passed as minimumValues are sorted, since they are constants in my domain. If these values can change, the minimumValues list should be sorted again.
Finally, there is a test that uses if statements to get to the same result (which is probably more flexible if you need something else than the index).
[TestClass]
public class RangeUnitTests
{
[DataTestMethod]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, -1, 0)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 0, 1)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 1, 1)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 5, 1)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 7, 2)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 15, 2)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 16, 3)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 30, 3)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 31, 4)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 100, 4)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 101, 5)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 500, 5)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 501, 6)]
public void Use_BinarySearch_To_Determine_Range(int[] minimumValues, int inputValue, int expectedRange)
{
var list = minimumValues.ToList();
var index = list.BinarySearch(inputValue);
if (index < 0)
{
index = ~index;
}
Assert.AreEqual(expectedRange, index);
}
[DataTestMethod]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, -1, 0)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 0, 1)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 1, 1)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 5, 1)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 7, 2)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 15, 2)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 16, 3)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 30, 3)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 31, 4)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 100, 4)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 101, 5)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 500, 5)]
[DataRow(new[] { -1, 5, 15, 30, 100, 500 }, 501, 6)]
public void Use_Ifs_To_Determine_Range(int[] _, int inputValue, int expectedRange)
{
int actualRange = 6;
if (inputValue < 0)
{
actualRange = 0;
}
else if (inputValue <= 5)
{
actualRange = 1;
}
else if (inputValue <= 15)
{
actualRange = 2;
}
else if (inputValue <= 30)
{
actualRange = 3;
}
else if (inputValue <= 100)
{
actualRange = 4;
}
else if (inputValue <= 500)
{
actualRange = 5;
}
Assert.AreEqual(expectedRange, actualRange);
}
}
I did a little perfomance testing by duplicating the initial set [DataRow] several times (up to 260 testcases for each method). I did not see a significant difference in performance with these parameteres. Note that I ran each [DataTestMethod] in a seperate session. Hopefully this balances out any start-up costs that the test framework might add to first test that is executed.

You could check out the powercollections here found on codeplex that has a collection that can do what you are looking for.
Hope this helps,
Best regards,
Tom.

Related

Google Or-Tools Routing - Solution is null

I am trying to implement VRPTW of Google OR-Tools. But I am facing an issue. When I pass the dynamic Time matrix then the solution object is null but when I pass the Time matrix which is given in example then it worked.
Here is my code
public class DataModel
{
public long[,] DistanceMatrix { get; set; }
public long[,] TimeMatrix = {
//commented matrix is dynamic generated
// {0,5,20,10,0,5},
//{5,0,25,10,5,5},
//{20,25,0,30,20,20},
//{10,10,30,0,10,15},
//{0,5,20,10,0,5},
//{5,5,20,15,5,0},
{0, 6, 9, 8, 7, 3},
{6, 0, 8, 3, 2, 6},
{9, 8, 0, 11, 10, 6},
{8, 3, 11, 0, 1, 7},
{7, 2, 10, 1, 0, 6},
{3, 6, 6, 7, 6, 0},
};
public long[,] TimeWindows = {
{0, 5}, // depot
{7, 12}, // 1
{10, 15}, // 2
{16, 18}, // 3
{10, 13}, // 4
{0, 5}, // 5
};
public int VehicleNumber = 3;
public int Depot = 0;
};
Here is the main function code
DataModel data = new DataModel();
// data.TimeMatrix = TimeMatrix;
// Create Routing Index Manager
RoutingIndexManager manager = new RoutingIndexManager(
data.TimeMatrix.GetLength(0),
data.VehicleNumber,
data.Depot);
// Create a Routing Model.
RoutingModel routing = new RoutingModel(manager);
// Create and register a transit callback.
int transitCallbackIndex = routing.RegisterTransitCallback(
(long fromIndex, long toIndex) =>
{
// Convert from routing variable Index to distance matrix NodeIndex.
var fromNode = manager.IndexToNode(fromIndex);
var toNode = manager.IndexToNode(toIndex);
return data.TimeMatrix[fromNode, toNode];
}
);
// Define the cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
// Add Distance constraint.
routing.AddDimension(
transitCallbackIndex, // transit callback
30, // allow waiting time
30, // vehicle maximum capacities
false, // start cumul to zero
"Time");
RoutingDimension timeDimension = routing.GetMutableDimension("Time");
// Add time window constraints for each location except depot.
for (int i = 1; i < data.TimeWindows.GetLength(0); ++i)
{
long index = manager.NodeToIndex(i);
timeDimension.CumulVar(index).SetRange(
data.TimeWindows[i, 0],
data.TimeWindows[i, 1]);
}
// Add time window constraints for each vehicle start node.
for (int i = 0; i < data.VehicleNumber; ++i)
{
long index = routing.Start(i);
timeDimension.CumulVar(index).SetRange(
data.TimeWindows[0, 0],
data.TimeWindows[0, 1]);
}
// Instantiate route start and end times to produce feasible times.
for (int i = 0; i < data.VehicleNumber; ++i)
{
routing.AddVariableMinimizedByFinalizer(
timeDimension.CumulVar(routing.Start(i)));
routing.AddVariableMinimizedByFinalizer(
timeDimension.CumulVar(routing.End(i)));
}
// Setting first solution heuristic.
RoutingSearchParameters searchParameters =
operations_research_constraint_solver.DefaultRoutingSearchParameters();
searchParameters.FirstSolutionStrategy =
FirstSolutionStrategy.Types.Value.PathCheapestArc;
// Solve the problem.
Assignment solution = routing.SolveWithParameters(searchParameters); //it is null when dynamic time matrix is used, but it is not null when time matrix mentioned in example is used.
The problem seems to be in AddDimension Method. I am struck in this but could not find any solution. Please suggest any solution.
Solution is null indicates that the solver was not able to find any feasible solution. Most likely your time windows are too tight.
Either try relaxing your time windows
or make sure nodes are optional (using addDisjunction).

Use linq to select a range that overlaps another range

I have a list that contains a number of 'bands', as follows:
var bands = new List<Band>();
bands.Add(new Band(1, 12, 1, 100, 199, 100, 292));
bands.Add(new Band(1, 5, 1, 200, 1000, 100, 292));
bands.Add(new Band(6, 6, 1, 200, 2000, 100, 210));
bands.Add(new Band(7, 7, 1, 200, 2000, 100, 192));
bands.Add(new Band(8, 8, 1, 200, 2000, 100, 178));
bands.Add(new Band(9, 9, 1, 200, 2000, 100, 167));
bands.Add(new Band(10, 10, 1, 200, 2000, 100, 158));
bands.Add(new Band(11, 11, 1, 200, 2000, 100, 150.5));
bands.Add(new Band(12, 12, 1, 200, 999, 100, 140));
bands.Add(new Band(12, 18, 3, 1000, 3000, 100, 71.3));
bands.Add(new Band(24, 24, 6, 1000, 3000, 100, 71.3));
The important columns are 4 and 5 - the first band values being 100 and 199.
Presently, I have a query that selects one or more bands based on a single parameter:
public static IEnumerable<Quote> ForAmount(int amount)
{
return Repository.Bands
.Where(x => amount >= x.MinAmount && amount <= x.MaxAmount)
.YieldTerms(amount); // this does something with the selected data
}
However, I'd like to pull out bands that fall within a low and high range.
New signature:
public static IEnumerable<Quote> ForAmount(int lowAmount, int highAmount)
{
// Query
}
So, for instance Low = 100 and High = 500.
Given the example high/low values, the following band would be selected (100 - 500 overlaps 100 - 199).
bands.Add(new Band(1, 12, 1, 100, 199, 100, 292));
So would the following band (100 - 500 overlaps 200 - 2000).
bands.Add(new Band(1, 5, 1, 200, 1000, 100, 292));
I'm sure this is easily done, but I've got brain fog right now, so any help appreciated.
Repository.Bands
.Where(x => x.MinAmount <= High && x.MaxAmount >= Low)
should do it
I have demonstrated something similar to your example in dotnet fiddle;
https://dotnetfiddle.net/dHr2Nn
This should be working.
public static IEnumerable<Quote> ForAmount(int lowAmount, int highAmount)
{
return Repository.Bands
.Where(x => x.MinAmount <= highAmount && x.MinAmount >= lowAmount)
|| (x.MaxAmount >= lowAmount && x.MaxAmount <= highAmount))
.YieldTerms(...); // this does something with the selected data
}

C# how to include 0 in Roulette Simulation?

So I want to simulate a roulette to proof that the House always wins.
I’m almost done but I stumbled upon a problem. I’m able to enter how many times to roll and it works fine. I get different numbers and it also tells me if red or black.
However the number 0 won’t show up in the results. I don’t know how to fix this, the code looks fine to me.
Code:
namespace ConsoleApplication9
{
class Program
{
static void Main(string[] args)
{
int[] Null = new int[1] { 0 };
int[] Rote = new int[18] { 1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36 };
int[] Schwarze = new int[18] { 2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35 };
// 0 ohne Tischlimit
var list = new List<int>();
list.AddRange(Rote);
list.AddRange(Schwarze);
list.AddRange(Null);
Console.WriteLine("Wie oft soll gespielt werden?");
int Anzahl = Convert.ToInt32(Console.ReadLine());
Random zufall = new Random();
for (int i = 0; i < Anzahl; ++i)
{
int number = list[zufall.Next(0, list.Count - 1)];
if (Rote.Contains(number))
{
Console.WriteLine("Rot" + number);
}
if (Schwarze.Contains(number))
{
Console.WriteLine("Schwarz" + number);
}
if (Null.Contains(number))
{
Console.WriteLine("Null" + number);
}
}
Console.ReadLine();
}
}
Ok, the thing is that Random.Next Method (Int32, Int32) uses upper bound as exclusive. So you have 0 as last element of list. And passing list.Count - 1 results in generating values between 0 and list.Count - 2. So the last element of the list is just ignored as you will never generate the last index list.Count - 1. You need to pass list.Count to Next method:
int number = list[zufall.Next(0, list.Count)];
https://msdn.microsoft.com/en-us/library/2dx6wyd4(v=vs.110).aspx
The Next(Int32, Int32) overload returns random integers that range
from minValue to maxValue – 1

Escape c# class property from JSON serialization (to remove quotes)

I EDITED THE SOLUTION BELOW THE QUESTION !!!
I have read like 30 similar articles on this, but nothing quite hits the spot.
My scenario: in order to feed a charting library (highcharts/highstock) with data in ASP.NET I manually created the basic classes for such a graph. This is due to the fact that all the wrapper projects work for highcharts but NOT for highstock. However, highstock requires a HTML script block like this (working):
<script>
$('#container2').highcharts('StockChart',
{
"title": {
"text": "Graph_Title_string"
},
"yAxis": {
"title": {
"text": "YAxis_Title_string"
},
"height": 200,
"lineWidth": 2,
"top": 80
},
"series": [
{
"type": "line",
"name": "Testdata",
"data": [[Date.UTC(2014, 3, 8, 0, 0), 3], [Date.UTC(2014, 3, 9, 0, 0), 2], [Date.UTC(2014, 3, 10, 0, 0), 4], [Date.UTC(2014, 3, 11, 0, 0), 4], [Date.UTC(2014, 3, 12, 0, 0), 3], [Date.UTC(2014, 3, 13, 0, 0), 4], [Date.UTC(2014, 3, 14, 0, 0), 2], [Date.UTC(2014, 3, 15, 0, 0), 1], [Date.UTC(2014, 3, 16, 0, 0), 4], [Date.UTC(2014, 3, 17, 0, 0), 0]]
}]
});
I created the c# classes and their properties, as for example:
public class Series
{
public string type { get; set; }
public string name { get; set; }
public object data { get; set; }
}
Later on I use JsonConvert.SerializeObject to serialize my chart-object (with instanced classes title, yAxis, series etc within), which results in following output:
{
"title": {
"text": "Graph_Title_string"
},
"yAxis": {
"title": {
"text": "YAxis_Title_string"
},
"height": 200,
"lineWidth": 2,
"top": 0
},
"series": [
{
"type": "line",
"name": "Testdata",
"data": "[[Date.UTC(2014, 3, 8, 0, 0), 3],[Date.UTC(2014, 3, 9, 0, 0), 2],[Date.UTC(2014, 3, 10, 0, 0), 0],[Date.UTC(2014, 3, 11, 0, 0), 4],[Date.UTC(2014, 3, 12, 0, 0), 4],[Date.UTC(2014, 3, 13, 0, 0), 2],[Date.UTC(2014, 3, 14, 0, 0), 1],[Date.UTC(2014, 3, 15, 0, 0), 1],[Date.UTC(2014, 3, 16, 0, 0), 0],[Date.UTC(2014, 3, 17, 0, 0), 3]]"
}
]
}
So the problem is: the value of series->data is enclosed in quotes. As highstock obviously requires an object array as data ([[DateTime, value],[DateTime, value],...etc]), the chart is not rendered unless I remove these quotes around the array.
When the array was "pure" integer there would be no quotes (I guess), but as my points need to be DateTime/value I need an array of objects.
Therefore the question: how can I force my JSON serializer to NOT enclose my value of an object array in quotes?
Maybe this is trivial and simple, and I was looking to far. As said, I've read lots of articles on similar problems, but nothing worked for me. Any help highly appreciated!
SOLUTION:
the data-array in my series-node is int/double - DATE.UTC(2014, 3, 8, 0, 0) doesn't return a DateTime but a NUMBER (reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC)
Therefore defining a double array in my behind-class will result in the desired output format (as seen in the great suggestion below by craig and also the marked answer) without the quotation marks.
Code (reference: http://forums.asp.net/post/1463013.aspx ... however, slightly modified) for converting c# DateTime to the required milliseconds-timestamp:
public double MilliTimeStamp(DateTime d2)
{
DateTime d1 = new DateTime(1970, 1, 1);
TimeSpan ts = new TimeSpan(d2.Ticks - d1.Ticks);
return ts.TotalMilliseconds;
}
I created a class defined as follows:
public class Series
{
public string type { get; set; }
public string name { get; set; }
public List<object> data { get; set; }
}
Then used the following code to populate it and convert it to JSON.
Series series = new Series();
series.type = "foo";
series.name = "bar";
series.data = new List<object>();
for( int i = 0; i < 5; i++ )
{
series.data.Add( DateTime.Now );
series.data.Add( i );
}
var json = JsonConvert.SerializeObject( series );
The resulting JSON was:
{"type":"foo","name":"bar","data":["2014-04-17T07:47:16.3620755-07:00",0,"2014-0
4-17T07:47:16.3620755-07:00",1,"2014-04-17T07:47:16.3620755-07:00",2,"2014-04-17
T07:47:16.3620755-07:00",3,"2014-04-17T07:47:16.3620755-07:00",4]}
P.S. Declare data as object[] also generated the above JSON.
Answer from #CraigW. is very close. To make it closer to what you are after, try to declare data as collection of collection of object :
public class Series
{
public string type { get; set; }
public string name { get; set; }
public List<List<object>> data { get; set; }
}
Test code to populate the model :
Series series = new Series();
series.type = "foo";
series.name = "bar";
series.data = new List<List<object>>();
for (int i = 0; i < 5; i++)
{
var data = new List<object>();
data.Add(DateTime.Now);
data.Add(i);
series.data.Add(data);
}
var json = JsonConvert.SerializeObject(series, Formatting.Indented);
Test result :
{
"type": "foo",
"name": "bar",
"data": [
[
"2014-04-17T22:15:06.8812404+07:00",
0
],
[
"2014-04-17T22:15:06.8812404+07:00",
1
],
[
"2014-04-17T22:15:06.8812404+07:00",
2
],
[
"2014-04-17T22:15:06.8812404+07:00",
3
],
[
"2014-04-17T22:15:06.8812404+07:00",
4
]
]
}

How to group the same values in a sequence with LINQ?

I have a sequence. For example:
new [] { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 }
Now I have to remove duplicated values without changing the overall order. For the sequence above:
new [] { 10, 1, 5, 25, 45, 40, 100, 1, 2, 3 }
How to do this with LINQ?
var list = new List<int> { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
List<int> result = list.Where((x, index) =>
{
return index == 0 || x != list.ElementAt(index - 1) ? true : false;
}).ToList();
This returns what you want. Hope it helped.
var list = new List<int> { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
var result = list.Where((item, index) => index == 0 || list[index - 1] != item);
It may be technically possible (though I don't think you can with a one-liner) to solve this with LINQ, but I think it's more elegant to write it yourself.
public static class ExtensionMethods
{
public static IEnumerable<T> PackGroups<T>(this IEnumerable<T> e)
{
T lastItem = default(T);
bool first = true;
foreach(T item in e)
{
if (!first && EqualityComparer<T>.Default.Equals(item, lastItem))
continue;
first = false;
yield return item;
lastItem = item;
}
}
}
You can use it like this:
int[] packed = myArray.PackGroups().ToArray();
It's unclear from the question what should be returned in the case of 1,1,2,3,3,1. Most answers given return 1,2,3, whereas mine returns 1,2,3,1.
You can use Contains and preserve order
List<int> newList = new List<int>();
foreach (int n in numbers)
if (newList.Count == 0 || newList.Last() != n)
newList.Add(n);
var newArray = newList.ToArray();
OUTPUT:
10, 1, 5, 25, 45, 40, 100, 1, 2, 3
Did you try Distinct?
var list = new [] { 10, 20, 20, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
list = list.Distinct();
Edit: Since you apparently only want to group items with the same values when consecutive, you could use the following:
var list = new[] { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
List<int> result = new List<int>();
foreach (int item in list)
if (result.Any() == false || result.Last() != item)
result.Add(item);

Categories

Resources