Use linq to select a range that overlaps another range - c#

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
}

Related

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

Fastest algorithm to merge 2 arrays such that elements are interlocked at even intervals

Requirements:
Integer operations only (no floats)
Elements are interlocked at intervals as evenly as possible
Note:
"Intervals as evenly as possible" can be defined as having each length of intervals as close to one value as possible.
Micro-optimizations are welcome and desired.
Example inputs and outputs:
//Inputs
[ 1, 2, 3, 4, 5, 6, 7 ]
[ 10, 20, 30, 40 ]
//Correct output
[ 1, 10, 2, 20, 3, 30, 4, 5, 40, 6, 7]
//Wrong output ([5, 6, 7] is not an optimal interval)
[ 1, 10, 2, 20, 3, 30, 4, 40, 5, 6, 7]
-
//Inputs
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[ 2, 2, 2 ]
//Correct output
[ 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 3, 1, 1]
//Wrong output (last [1] is not an optimal interval)
[ 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1]
Here's my own implementation with as much optimization as I can think of for a managed language. In C++, it may be faster to use a triple XOR swap for the array pointers, but I'm not sure. It may be necessary to look at the JITed assembly to further optimize this particular code.
In the meantime, let's see if other people have better algorithms.
int[] InterlockMerge(int[] a1, int[] a2) {
var longSet = a1;
var shortSet = a2;
//Swap if a2 is longer
if (a1.Length < a2.Length){
longSet = a2;
shortSet = a1;
}
var ll = longSet.Length;
var ls = shortSet.Length;
var totalLength = ll + ls;
int[] res = new int[totalLength]; //The resulting set
int l = ll / (ls + 1); //Initial testing ratio (an int)
int li = 0; //index for longSet
int si = 0; //index for shortSet
for (int i = 0; i < totalLength; i++) {
if (l > 0) {
res[i] = longSet[li++];
l--;
continue;
}
res[i] = shortSet[si++];
l = (ll - li) / (ls - si + 1); //Recalculate the testing ratio
}
return res;
}

Filter C# collection of float given a minimum difference value between adjacent elements

Suppose I have an ordered list of float (ascending).
I want to remove from it each element whose difference between next element and itself is less than a given threshold.
I need something like this:
List<float> orderedList;
IEnumerable<float> query = orderedList.Where(currentNum , nextNum => nextNum - currentNum < threshold);
Is that possible? If yes how?
Try this out:
var filteredElements = new List<float>();
float ? prev = null;
orderedList.ToList().ForEach((e)=>{
if (prev.HasValue)
{
if (e-prev >= threshold)
filteredElements.Add(prev.Value);
}
prev = e
});
Try this -
List<float> orderedList = new List<float>() { 12, 14, 34, 45 };
List<float> itemsToRemove = orderedList.Where((item, index) =>
index < orderedList.Count - 1 &&
orderedList[index + 1] - item < threshhold).ToList();
Try this instead :
var ls1 = ls.Where((item, index) =>
item - ls[ls.Count == index + 1 ? index : index + 1] >= 0);
Hope this will help !!
This appears to work. (Though there are some potentially misunderstood implications of your question.)
var input = new List<float>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 18, 21, 24, 27, 29, 35, 40, 46, 59 };
var output = input.Zip(input.Skip(1).Concat(new[]{float.MaxValue}), (a, b) => new { a, b }).Where(x => x.b - x.a > 2).Select(x => x.a);
This produces the output of:
15, 18, 21, 24, 29, 35, 40, 46, 59
This has the advantage of working with any IEnumerable.

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);

A dictionary object that uses ranges of values for keys

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.

Categories

Resources