c# range lookup within a collection - c#

I have searched through the StackOverflow yet couldn't find my question.
I have some data like this:
object a -> min:3 max:13
object b -> min:11 max:20
object c -> min:16 max:21
...
z-> min:200 max:250
For a specified interval, I expect a, b, c or other objects or a list.
For example if (6,8) is passed then I want to have "a", if (12,13) is passed I want to have a list of "a and b", if (17, 20) is passed I want to have a list of "b and c" and if (3,250) then I want to have a list of all.
I don't know in which type of collection I should store the values (3, 13, object a) and others.
Can you name the collection and give an example?
Thanks in advance...
p.s. sorry if I couldn't describe well because of my English and thank you all

So you want to find objects where the min value is smaller/equal the passed min-value and the max value is larger/equal the passed max-value.
var query = objects.Where(obj=> obj.MinVal <= minVal && obj.MaxVal >= maxVal);
Can you name the collection and give an example?
So you don't have a collection? You should fill a List<Range> where Range is a custom class with at least two properties MinVal and MaxVal.

void Main()
{
var input = new List<Interval>
{
new Interval { Name = "a", Min = 3, Max = 13 },
new Interval { Name = "b", Min = 11, Max = 20 },
new Interval { Name = "c", Min = 16, Max = 21 },
new Interval { Name = "z", Min = 200, Max = 250 }
};
var interval = new Interval { Name = "search", Min = 12, Max = 13 };
// Don't forget the third case when the interval
// you're looking for is inside your input intervals
// Min = 210, Max = 220 should return "z"
var result = input.Where(i => (interval.Min <= i.Min && i.Min <= interval.Max) ||
(interval.Min <= i.Max && i.Max <= interval.Max) ||
(i.Min <= interval.Min && interval.Max <= i.Max));
}
class Interval
{
public string Name;
public int Min;
public int Max;
}

You can either create your own type or you can use Tuple<int, int> to represent one object. Then you can create and populate a List of these objects to store your entire collection. After that you can use LINQ to query for the desired objects:
List<Tuple<int, int>> YourCollection = new List<Tuple<int, int>>();
YourCollection.Add(new Tuple<int, int>(3, 13));
YourCollection.Add(new Tuple<int, int>(11, 20));
YourCollection.Add(new Tuple<int, int>(16, 21));
var Results = YourCollection.Where(x => x.Item1 <= MAX && MIN <= x.Item2);
where MIN and MAX define the range that you're interested in. Note that the condition above looks for overlapping (intersection) as appears to be what is needed.

sing a fork of #aush code it will be bettler if you can inherint the class System.Collections.CollectionBase then you can make an easy foreach implementing this class.
structure Interval{
public string Name;
public int Min;
public int Max;
public Interval(string Name,int Min,int Max){
this.Name = Name;
this.Min = Min;
this.Max = Max;
}
public bool IsInsideOfRange(int value){
if(value >= this.Min && value <= this.Max){
return true;
}else{
return false;
}
}
public overrides ToString(){
return this.Name;
}
}
class IntervalCollection : System.Collections.CollectionBase {
public void Add(string Name,int Min,int Max){
Interval Item = new Interval(Name,Min,Max);
this.List.Add(Item);
}
public void Add(Interval Item){
this.List.Add(Item);
}
public string Encode(param int[] values){
string EcodedText = "";
foreach(int iValue in values){
foreach(Interval Item in this){
if(Item.IsInsideOfRange(iValue)){
EncodedText +=Item.ToString();
}
}
}
return EcodedText;
}
}
you can implement this class like this
IntervalCollection Intervals = new IntervalCollection();
string EncodeText = "";
Intervals.Add(new Interval { Name = "a", Min = 3, Max = 13 });
Intervals.Add(new Interval { Name = "b", Min = 11, Max = 20 });
Intervals.Add(new Interval { Name = "c", Min = 16, Max = 21 });
Intervals.Add( "z", 200, 250 }); //you can add item in this way too.
EncodeText = Intervals.Encode(6,8,12,13,17,20);

Related

Comparison process with enum element values

Enum A
{ a = 10, a1 = 35, ....}
Enum B
{ b = 5, b1 = 20, ..... }
How can I get the int values ​​of any two of the two enum class elements? To compare with these values ​​and destroy the smaller one.
Sorry. Maybe this is what you're looking for?
enumA.CompareTo(enumB) > 0 ? "A is greater than B" : "B is greater than or equal to A";
enumA.CompareTo(enumB) = 0 ? "A is equal to B" : "A is not equal to B";
This should compare two Enum instances (enumA and enumB), and execute the code inserted in place of the strings.
Take this console app as an example to obtain the lowest int from your enum type:
using System;
using System.Linq;
using System.Collections.Generic;
namespace ConsoleApp1 {
class Program {
enum Colors {
blue = 1,
orange = 2,
purple = 3
}
enum Stages {
level1 = 35,
level2 = 62,
level3 = 13
}
public static int getMinEnumValue(Type enumType) {
Array values = Enum.GetValues(enumType);
List<int> intList = ((int[])values).ToList();
int minValue = intList.Min();
return minValue;
}
static void Main(string[] args) {
int minStagegInt = getMinEnumValue(typeof(Stages));
int minColorInt = getMinEnumValue(typeof(Colors));
Console.WriteLine($"Min stage int: {minStagegInt}, which is: {Enum.GetName(typeof(Stages), minStagegInt)}");
Console.WriteLine($"Min color int: {minColorInt}, which is: {Enum.GetName(typeof(Colors), minColorInt)}");
Console.ReadLine();
//keyValue anonymous type
var myInstance1 = new[] { new { a = 10 }, new { a = 35 }, new { a = 3 } };
var myInstance2 = new[] { new { b = 5 }, new { b = 20 } };
var minObject1 = myInstance1.ToList().OrderBy(elem => elem.a).FirstOrDefault();
var minObject2 = myInstance2.ToList().OrderBy(elem => elem.b).FirstOrDefault();
Console.WriteLine($"Min a instance1: {minObject1.ToString()}");
Console.WriteLine($"Min b instance2: {minObject2.ToString()}");
Console.ReadLine();
}
}
}
Output:
Once you are able to get your min, or apply the logic you wish to your enum type, you can destroy or do whatever you want to your obtained enumeration intance of your enum type.
Enum can be cast to int
EnumA valA = EnumA.a; //10
EnumB valB = EnumB.b; //5
if((int)valA < (int)valB) // 10 < 5
{ Destroy(a);}
else {Destroy(b);}
Edit:
public static int CompareAtoB(int a, int b)
{
if(a==b) return 0;
return (a < b) ? 1 : -1;
}
You can now pass any value that you cast when you give as parameter.
int result = CompareAtoB((int)someEnum, (int) otherEnum);
Then act based on result.

Sort List of string which contains ranged numberd

Can anyone please help in build a logic to sort list of string which contains ranged number items.
Ex: List myList = new List() { "1", "3-6", "2", "8-10","7", "11", "13-18", "12"};
Sorted Output List:
1,
2,
3-6,
7,
8-10,
11,
12,
13-18
Note:
List contains only positive numbers.
Thanks,
Arjun
The basics step are:
Convert a string to int
Check if a string contains a separator
Split a String on a separator
Now here is a way to do it:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
static void Main(string[] args)
{
var myList = new List<string> { "1", "3-6", "2", "8-10", "7", "11", "13-18", "12" };
var ranges = myList.Select(x=> new Range(x)).ToList();
ranges.Sort();//Order yourself!
//Display
foreach (var range in ranges) Console.WriteLine(range);
//One line with comma separator
Console.WriteLine(string.Join(", ", ranges));
}
public class Range : IComparable<Range>
{
// Simple ctor, with Customisable separator in case of Negative Value.
public Range(string input, char separator= '-') {
if (!input.Contains(separator))// Is it a range?
{//It's not then
if (int.TryParse(input, out int value))// Lets try to convert it to an integer
{// it's a int then
Min = value; Max = value; // both min and max
}
else
{// It's not a range and it's not an int! Scream!
throw new ArgumentException(input + " is not a valid input !");
}
}
else
{//It's a range
var parts = input.Split(new char[] { separator }, 2);// split in 2 on the separator
if (int.TryParse(parts[0], out int part1) && int.TryParse(parts[1], out int part2))// Both part are int?
{
Min = Math.Min(part1, part2); // min is the min and max the max.
Max = Math.Max(part1, part2); // It's easier to compute min/max than try to fix a bad input.
}
else {// It's look like a range but those are not int! Scream!
throw new ArgumentException(input + " is not a valid input !" );
}
}
}
public int Min { get; set; }
public int Max { get; set; }
// Comparing bound together. {1-1} < {1-2} < {1-6} < {2-2};
public int CompareTo(Range obj)
{
var mincomparer = this.Min.CompareTo(obj.Min);
var maxcomparer = this.Max.CompareTo(obj.Max);
return mincomparer == 0 ? maxcomparer : mincomparer;
}
public override string ToString()
{
return Min == Max ? Min.ToString() : Min + "-" + Max;
}
}
}
LiveDemo

Merge interval with merge distance in C#

Given a list of intervals [start, end] I have to merge them based on a specified merge distance. The intervals are arriving in a particular order, and as they arrive it should merge them according to the specified merge distance as each interval is received. Some of these intervals will be removed (in the arrival stream they will be marked as removed) – in that situation I've to treat the original interval as if it never existed. Example:
Merge distance is 7 – in the following example the input is arriving in order
I've tried the following algorithm to merge them but my Output isn't coming like the above example. Can somebody assists me, what I'm missing here!
Here is my code.
class Program
{
static void Main(string[] args)
{
var intervals = new List<Interval>
{
new Interval
{
start = 1,
end = 20
},
new Interval
{
start = 55,
end = 58
},
new Interval
{
start = 60,
end = 89
},
new Interval
{
start = 15,
end = 31
},
new Interval
{
start = 10,
end = 15
},
new Interval
{
start = 1,
end = 20
}
};
var mergedIntervals = Merge(intervals, 7);
foreach (var item in mergedIntervals)
{
Console.WriteLine($"[{item.start}, {item.end}]");
}
Console.ReadKey();
}
public static List<Interval> Merge(List<Interval> intervals, int mergeDistance)
{
var result = new List<Interval>();
for (int i = 0; i < intervals.Count; i++)
{
var newInterval = new Interval(intervals[i].start, intervals[i].end);
//while (i < intervals.Count - 1 && newInterval.end >= intervals[i + 1].start)
while (i < intervals.Count - 1 && newInterval.end <= mergeDistance) // intervals[i + 1].start)
{
newInterval.end = Math.Max(newInterval.end, intervals[i + 1].end);
i++;
}
result.Add(newInterval);
}
return result;
}
}
public class Interval
{
public int start { get; set; }
public int end { get; set; }
public Interval()
{
}
public Interval(int start, int end)
{
this.start = start;
this.end = end;
}
}
Can you please try this code, working demo code here
class Program
{
static void Main(string[] args)
{
var intervals = new List<Interval>
{
new Interval
{
start = 1,
end = 20,
isAdded = true
},
new Interval
{
start = 55,
end = 58,
isAdded = true
},
new Interval
{
start = 60,
end = 89,
isAdded = true
},
new Interval
{
start = 15,
end = 31,
isAdded = true
},
new Interval
{
start = 10,
end = 15,
isAdded = true
},
new Interval
{
start = 1,
end = 20,
isAdded = false
}
};
var mergedIntervals = Merge(intervals, 7);
foreach (var item in mergedIntervals)
{
Console.WriteLine($"[{item.start}, {item.end}]");
}
Console.ReadKey();
}
public static List<Interval> Merge(List<Interval> intervals, int mergeDistance)
{
var result = new List<IntervalGroup>();
var group = new IntervalGroup();
foreach (var item in intervals)
{
group = result.Where(c => c.Groups.Any(g =>
Math.Abs(g.end - item.start) <= mergeDistance ||
Math.Abs(g.end - item.end) <= mergeDistance)).FirstOrDefault();
if (group != null && item.isAdded)
{
group.Groups.Add(item);
}
else if(item.isAdded)
{
group = new IntervalGroup();
group.Groups = new List<Interval>();
result.Add(group);
group.Groups.Add(item);
}
else if(item.isAdded == false)
{
group.Groups.Remove(group.Groups.Where(c => c.start == item.start && c.end == item.end).First());
}
}
var finalResult = result.Select(s => new Interval { start = s.Groups.Min(min => min.start), end = s.Groups.Max(min => min.end) });
return finalResult.ToList();
}
}
public class Interval
{
public int start { get; set; }
public int end { get; set; }
public bool isAdded { get; set; }
public Interval()
{
}
public Interval(int start, int end, bool isAdded)
{
this.start = start;
this.end = end;
this.isAdded = isAdded;
}
}
public class IntervalGroup
{
public List<Interval> Groups { get; set; }
}
When a problem is beyond your grasp, it is very helpful to break it up into smaller pieces and code the bits that you do understand. Here's how I break it down, step by step.
First, I think it would be convenient to be able to check the length of an interval, so let's add a property.
class Interval
{
/* Prior code */
public int Length => this.end - this.start;
Now let's write a method that merges two intervals:
class Interval
{
/* Prior code */
static public Interval Merge(Interval a, Interval b )
{
return new Interval(Math.Min(a.start, b.start), Math.Max(a.end, b.end));
}
Now we need to write the code that decides if two intervals are capable of being merged. The prototype could look like this:
static public bool CanMerge(Interval a, Interval b, int mergeDistance)
What logic do we need inside? Well, we could check for overlaps and check the merge distance from both ends, but I know a shortcut. Given a merge A + B = C, the merge is allowed if and only if the length of C is less than or equal to the sum of A + B + the merge distance. So we can write this:
class Interval
{
/* Prior code */
static public bool CanMerge(Interval a, Interval b, int mergeDistance)
{
var merged = Merge(a,b);
var canMerge = merged.Length <= a.Length + b.Length + mergeDistance;
return canMerge;
}
From there you can add to a list by checking for mergeable items. Note that recursion is required because the act of merging an interval could result in another interval becoming mergeable.
void AddToList(List<Interval> list, Interval newInterval, int mergeDistance)
{
var target = list.FirstOrDefault( x => Interval.CanMerge(x, newInterval, mergeDistance) );
if (target == null)
{
list.Add(newInterval);
return;
}
list.Remove(target);
AddToList(list, Interval.Merge(target, newInterval), mergeDistance);
}

Build up List from another List

I've got a list of Players. Each Player has a Marketvalue. I need to build up a second list which iterates through the player list and builds up a team. The tricky thing is the new team should have at least 15 players and a maximum Marketvalue of 100 Mio +/- 1%.
Does anyone know how to do that elegantly?
private Result<List<Player>> CreateRandomTeam(List<Player> players, int startTeamValue)
{
// start formation 4-4-2
// Threshold tw 20 mio defender 40 mio Midfielder 40 Mio Striker 50 Mio
var playerKeeperList = players.FindAll(p => p.PlayerPosition == PlayerPosition.Keeper);
var playerDefenderList = players.FindAll(p => p.PlayerPosition == PlayerPosition.Defender);
var playerMidfieldList = players.FindAll(p => p.PlayerPosition == PlayerPosition.Midfield);
var playerStrikerList = players.FindAll(p => p.PlayerPosition == PlayerPosition.Striker);
List<Player> keeperPlayers = AddRandomPlayers(playerKeeperList, 2, 0, 20000000);
List<Player> defenderPlayers = AddRandomPlayers(playerDefenderList, 4, 0, 40000000);
List<Player> midfieldPlayers = AddRandomPlayers(playerMidfieldList, 4, 0, 40000000);
List<Player> strikerPlayers = AddRandomPlayers(playerStrikerList, 2, 0, 50000000);
List<Player> team = new List<Player>();
team.AddRange(keeperPlayers);
team.AddRange(defenderPlayers);
team.AddRange(midfieldPlayers);
team.AddRange(strikerPlayers);
var currentTeamValue = team.Sum(s => s.MarketValue);
var budgetLeft = startTeamValue - currentTeamValue;
players.RemoveAll(p => team.Contains(p));
var player1 = AddRandomPlayers(players, 2, 0, budgetLeft);
team.AddRange(player1);
players.RemoveAll(p => player1.Contains(p));
currentTeamValue = team.Sum(t => t.MarketValue);
budgetLeft = startTeamValue - currentTeamValue;
var player2 = players.Aggregate((x, y) => Math.Abs(x.MarketValue - budgetLeft) < Math.Abs(y.MarketValue - budgetLeft) ? x : y);
team.Add(player2);
players.Remove(player2);
return Result<List<Player>>.Ok(team);
}
private static List<Player> AddRandomPlayers(List<Player> players, int playerCount, double minMarketValue, double threshold)
{
// TODO: AYI Implement Random TeamName assign logic
Random rnd = new Random();
var team = new List<Player>();
double assignedTeamValue = 0;
while (team.Count < playerCount)
{
var index = rnd.Next(players.Count);
var player = players[index];
if ((assignedTeamValue + player.MarketValue) <= threshold)
{
team.Add(player);
players.RemoveAt(index);
assignedTeamValue += player.MarketValue;
}
}
return team;
}`
This isn't really a C# question so much as an algorithm question, so there may be a better place for it. AIUI, you want to pick 15 numbers from a list, such that the total adds up to 99-101.
It's likely that there are many solutions, all equally valid.
I think you could do it like this:
Build a list of the 14 cheapest items.
Pick the highest value, so long as the remaining space is greater than the total of the 14 cheapest.
Repeat the above, skipping any players that won't fit.
Fill the remaining places with players from the 'cheapest' list.
This will probably give you a team containing the best and worst players, and one middle-ranking player that just fits.
If you want to do some more research, this sounds like a variant of the coin change problem.
Just to show my solution if someone need's it.
var selection = new EliteSelection();
var crossover = new OnePointCrossover(0);
var mutation = new UniformMutation(true);
var fitness = new TeamFitness(players, startTeamValue);
var chromosome = new TeamChromosome(15, players.Count);
var population = new Population(players.Count, players.Count, chromosome);
var ga = new GeneticAlgorithm(population, fitness, selection, crossover, mutation)
{
Termination = new GenerationNumberTermination(100)
};
ga.Start();
var bestChromosome = ga.BestChromosome as TeamChromosome;
var team = new List<Player>();
if (bestChromosome != null)
{
for (int i = 0; i < bestChromosome.Length; i++)
{
team.Add(players[(int) bestChromosome.GetGene(i).Value]);
}
// Remove assigned player to avoid duplicate assignment
players.RemoveAll(p => team.Contains(p));
return Result<List<Player>>.Ok(team);
}
return Result<List<Player>>.Error("Chromosome was null!");
There is a fitness method which handles the logic to get the best result.
class TeamFitness : IFitness
{
private readonly List<Player> _players;
private readonly int _startTeamValue;
private List<Player> _selected;
public TeamFitness(List<Player> players, int startTeamValue)
{
_players = players;
_startTeamValue = startTeamValue;
}
public double Evaluate(IChromosome chromosome)
{
double f1 = 9;
_selected = new List<Player>();
var indexes = new List<int>();
foreach (var gene in chromosome.GetGenes())
{
indexes.Add((int)gene.Value);
_selected.Add(_players[(int)gene.Value]);
}
if (indexes.Distinct().Count() < chromosome.Length)
{
return int.MinValue;
}
var sumMarketValue = _selected.Sum(s => s.MarketValue);
var targetValue = _startTeamValue;
if (sumMarketValue < targetValue)
{
f1 = targetValue - sumMarketValue;
}else if (sumMarketValue > targetValue)
{
f1 = sumMarketValue - targetValue;
}
else
{
f1 = 0;
}
var keeperCount = _selected.Count(s => s.PlayerPosition == PlayerPosition.Keeper);
var strikerCount = _selected.Count(s => s.PlayerPosition == PlayerPosition.Striker);
var defCount = _selected.Count(s => s.PlayerPosition == PlayerPosition.Defender);
var middleCount = _selected.Count(s => s.PlayerPosition == PlayerPosition.Midfield);
var factor = 0;
var penaltyMoney = 10000000;
if (keeperCount > 2)
{
factor += (keeperCount - 2) * penaltyMoney;
}
if (keeperCount == 0)
{
factor += penaltyMoney;
}
if (strikerCount < 2)
{
factor += (2 - strikerCount) * penaltyMoney;
}
if (middleCount < 4)
{
factor += (4 - middleCount) * penaltyMoney;
}
if (defCount < 4)
{
factor += (4 - defCount) * penaltyMoney;
}
return 1.0 - (f1 + factor);
}
}

Find adjacent/neighboring elements in a list of objects c#

I have a list of objects. I have a value, that is not in the list, but would like to find the adjacent elements, where that value would fit. Basically the previous and next element,like if this value was part of the list.
var previous = 0;
var next = 0;
decimal Value = 45M;
List<MileStone> items = new List<MileStone>();
items.Add(new MileStone() { CheckPoint = 0, Distance = 15.4M});// i = 0
items.Add(new MileStone() { CheckPoint = 20, Distance = 24.8M});// i = 1
items.Add(new MileStone() { CheckPoint = 40, Distance = 39.7M});// i = 2
items.Add(new MileStone() { CheckPoint = 60, Distance = 59.3M});// i = 3
items.Add(new MileStone() { CheckPoint = 80, Distance = 80.1M});// i = 4
In this example the 'Value' = 45, so that would fit between items[2] and item[3].
Desired outcome would be, a new list of the neighboring values:
List<MileStone> items2 = new List<MileStone>();
items2.Add(items[2]);
items2.Add(items[3]);
If 'Value' = 10 there should be only a one element in the items2 list, because there is no previous case.
List<MileStone> items2 = new List<MileStone>();
items2.Add(items[0]);
I would like to find an elegant, and nice solution for this, using LINQ Lambda expression.
I can find the indexes with a for loop, then add the desired elements to the items2 list, but would like to find a LINQ approach.
Any idea is welcomed!
Thanks!
If you really want to use Linq, one way to do this would be to order the list by the Distance property, then find the last item with a distance less than Value and the first item with a distance greater than Value, and add them to a List.
Here's a helper method that does that:
private static List<MileStone> GetNeighboringItems(decimal value,
List<MileStone> milestones)
{
var neighbors = new List<MileStone>();
if (milestones == null || milestones.Count == 0) return neighbors;
var orderedList = milestones.OrderBy(m => m.Distance).ToList();
var lesser = orderedList.LastOrDefault(m => m.Distance < value);
var greater = orderedList.FirstOrDefault(m => m.Distance > value);
if (lesser != null) neighbors.Add(lesser);
if (greater != null) neighbors.Add(greater);
return neighbors;
}
Then you can get your "neighbors" for Value = 45 and Value = 10:
private static void Main()
{
List<MileStone> items = new List<MileStone>
{
new MileStone {CheckPoint = 0, Distance = 15.4M},
new MileStone {CheckPoint = 20, Distance = 24.8M},
new MileStone {CheckPoint = 40, Distance = 39.7M},
new MileStone {CheckPoint = 60, Distance = 59.3M},
new MileStone {CheckPoint = 80, Distance = 80.1M}
};
Console.WriteLine("Neighbors of 45: {0}",
string.Join(", ", GetNeighboringItems(45, items)));
Console.WriteLine("Neighbors of 10: {0}",
string.Join(", ", GetNeighboringItems(10, items)));
Console.Write("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output
Note
I overrode ToString on the MileStone class so the Distance is shown for each item when written to the console:
public class MileStone
{
public int CheckPoint { get; set; }
public decimal Distance { get; set; }
public override string ToString()
{
return Distance.ToString();
}
}

Categories