I have two lists Schedule(Having Total Hours) And Registered (Having Time Intervals). I want to create a list that is having registered time covered with Schedule list total hours finished.
See the Upper image. The resultant list is covering Registered Times and ENd if it doesn't find any registration for left hours. It will just create further times according to hours left with their types.
Take a look at this sample:
class Task
{
public int duration;
public string type;
public int remaining;
public void Reset()
{
remaining = duration;
}
}
class Span
{
public int from;
public int to;
public int remaining;
public int duration => to - from;
public void Reset()
{
remaining = duration;
}
}
struct Assignment
{
public Span span;
public string type;
}
static IEnumerable<Assignment> AssignTasks(List<Task> tasks, List<Span> spans)
{
// add an infinite span to the end of list
spans.Add(new Span()
{
from = spans.Last().to,
to = int.MaxValue
});
// set remainings of tasks and spans by their total duration
foreach (Task task in tasks) { task.Reset(); }
foreach (Span span in spans) { span.Reset(); }
// set current task and span
int iTask = 0;
int iSpan = 0;
while (iTask < tasks.Count)
{
//find which is smaller: remaining part of current task, or
// remaining part of current span
int assigning =
tasks[iTask].remaining <= spans[iSpan].remaining ?
tasks[iTask].remaining : spans[iSpan].remaining;
// add a new assignment to results
yield return new Assignment()
{
span = new Span()
{
from = spans[iSpan].to - spans[iSpan].remaining,
to = spans[iSpan].to - spans[iSpan].remaining + assigning,
},
type = tasks[iTask].type
};
// update remaining parts of current task and span
tasks[iTask].remaining -= assigning;
spans[iSpan].remaining -= assigning;
// update counters if nothing is left
if (tasks[iTask].remaining == 0)
iTask++;
if (spans[iSpan].remaining == 0)
iSpan++;
}
}
In this piece of code, Task is equivalent to what you call "Scheduled", and Span is equivalent to "Registered". I removed From and To from Task because they seem irrelevant to the problem. I also added a Remaining field to both Task and Span classes, I use these to keep the unassigned part of tasks or spans since a portion of a task can be assigned to a portion of a span.
The key point, to make everything much easier, is adding an infinite span to the end of list of spans. Now there are more registered spans (resources) than our demands and we just need to simply assign them.
You can test it like this:
static void Main(string[] args)
{
List<Task> tasks = new List<Task>()
{
new Task() {duration = 4, type = "A"},
new Task() {duration = 2, type = "B"},
new Task() {duration = 6, type = "C"},
new Task() {duration = 8, type = "D"}
};
List<Span> spans = new List<Span>()
{
new Span() {from = 9, to = 10},
new Span() {from = 11, to = 13},
new Span() {from = 15, to = 20}
};
IEnumerable<Assignment> assignments = AssignTasks(tasks, spans);
Console.WriteLine("Tasks: duration, type");
foreach (Task task in tasks)
{
Console.WriteLine($"{task.duration}, {task.type}");
}
Console.WriteLine("\nSpans: from, to");
foreach (Span span in spans)
{
Console.WriteLine($"{span.from}, {span.to}");
}
Console.WriteLine("\nResults: from, to, type");
foreach (Assignment assignment in assignments)
{
Console.WriteLine($"{assignment.span.from}, {assignment.span.to}, {assignment.type}");
}
Console.ReadLine();
}
The outcome is:
Tasks: duration, type
4, A
2, B
6, C
8, D
Spans: from, to
9, 10
11, 13
15, 20
Results: from, to, type
9, 10, A
11, 13, A
15, 16, A
16, 18, B
18, 20, C
20, 24, C
24, 32, D
Here's my solution:
The following enum and classes represent the input format you requested.
public enum SType
{
A,
B,
C,
D
}
public class Schedule : Register
{
public SType Type { get; set; }
public Schedule(int from, int to, SType type) : base(from, to)
{
Type = type;
}
}
public class Register
{
public int From { get; set; }
public int To { get; set; }
public int TotalHours => To - From;
public Register(int from, int to)
{
From = from;
To = to;
}
}
Then, following your sample input:
var scheduled = new List<Schedule>()
{
new Schedule(0, 4, SType.A),
new Schedule(4, 6, SType.B),
new Schedule(6, 12, SType.C),
new Schedule(12, 20, SType.D)
};
var registered = new List<Register>()
{
new Register(9, 10),
new Register(11, 13),
new Register(15, 20)
};
This will retrieve the requested output:
var result = new List<Schedule>();
int elapsedHours = 0;
foreach (var s in scheduled)
{
int hours = s.TotalHours;
foreach (var r in registered)
{
if (elapsedHours <= r.To)
{
int from = Math.Max(elapsedHours, r.From);
if (r.TotalHours <= hours)
{
elapsedHours = r.To;
result.Add(new Schedule(from, elapsedHours, s.Type));
hours -= (elapsedHours - from);
}
else
{
elapsedHours = from + hours;
result.Add(new Schedule(from, elapsedHours, s.Type));
hours = 0;
}
}
}
if (hours > 0)
{
result.Add(new Schedule(elapsedHours, elapsedHours + hours, s.Type));
elapsedHours += hours;
}
}
Using the following classes to represent your source data:
public class Schedule {
public int From;
public int To;
public int TotalHours => To - From;
public string Type;
public Schedule(int from, int to, string type) {
From = from;
To = to;
Type = type;
}
}
public class Register {
public int From;
public int To;
public Register(int from, int to) {
From = from;
To = to;
}
}
Here is a for loop implementation that spreads each Schedule member across the Registered time intervals and then outputs the rest:
var ans = new List<Schedule>();
int currentRegistered = 0;
var rp = registered[currentRegistered];
var currentFrom = rp.From;
for (int curScheduled = 0; curScheduled < scheduled.Count; ++curScheduled) {
var s = scheduled[curScheduled];
for (var hours = s.TotalHours; hours > 0;) {
if (currentFrom >= rp.To)
rp = (++currentRegistered < registered.Count)
? registered[currentRegistered]
: new Register(currentFrom, int.MaxValue);
if (rp.From > currentFrom)
currentFrom = rp.From;
var newTo = (rp.To - currentFrom > hours) ? currentFrom + hours : rp.To;
ans.Add(new Schedule(currentFrom, newTo, s.Type));
hours -= newTo - currentFrom;
currentFrom = newTo;
}
}
Related
I am simulating an Iot Device (Noise Sensor) in Azure IoT hub the code below works perfectly fine.
However I want to simulate something closer to reality, where I can use different decibel ranges between different hours.
Something like this:
if 00.00- 7.00AM - Randum number between (10-20)
if 7am-9AM - Random number between (20-40)
if 11.30-1.30pm Random number between 60-80
I dont want to create a lof of IFs,Elses, as I would like to have cleaner code.
How should I do this in a structured manner?
My code below: (only the relevant method)
private static async Task SendDeviceToCloudMessagesAsync(CancellationToken ct)
{
// Initial telemetry values
int minNoise = 20;
int maxNoise = 90;
var rand = new Random();
while (!ct.IsCancellationRequested)
{
double noiseDecibels = rand.Next(minNoise, maxNoise);
// Create JSON message
string messageBody = JsonSerializer.Serialize(
new
{
eui= "58A0CB0000101DB6",
DecibelValue = noiseDecibels
});
using var message = new Message(Encoding.ASCII.GetBytes(messageBody))
{
ContentType = "application/json",
ContentEncoding = "utf-8",
};
// Add a custom application property to the message.
// An IoT hub can filter on these properties without
// access to the message body.
message.Properties.Add("noiseAlert", (noiseDecibels > 70) ? "true" : "false");
// Send the telemetry message
await s_deviceClient.SendEventAsync(message);
Console.WriteLine($"{DateTime.Now} > Sending message: {messageBody}");
await Task.Delay(60000);
}
}
I'm approaching this question in a data structure pattern (like you asked) so i would create a RangeHourDictionary class inheriting from Dictionary.
The Add method will add the ranges where the key will represent the start time and end time. The values will be an array of int of the size of 2 where the first value will represent the start range and the second value will be the end of the range.
Another function will be GetRandomRange which will get the current time and will return the value (which again, will represent the start and end of the random range)
public class RangeDictionary : Dictionary<Range, int[]>
{
public void Add(TimeSpan from, TimeSpan to, int[] randomValues)
{
Add(new Range(from, to), randomValues);
}
public int[] GetRandomRange(TimeSpan now)
{
try
{
return this.First(x => x.Key.From < now && x.Key.To > now).Value;
}
catch
{
return null;
}
}
}
public struct Range
{
public Range(TimeSpan from, TimeSpan to) : this()
{
From = from;
To = to;
}
public TimeSpan From { get; }
public TimeSpan To { get; }
}
//initialize
var lookup = new RangeDictionary();
lookup.Add(new TimeSpan(07, 0, 0), new TimeSpan(09, 0, 0), new int[2] { 10, 20 });
lookup.Add(new TimeSpan(09, 30, 0), new TimeSpan(11, 0, 0), new int[2] { 40, 50 });
lookup.Add(new TimeSpan(11, 0, 0), new TimeSpan(13, 0, 0), new int[2] { 60, 80 });
// call GetRandomRange
var res = lookup.GetRandomRange(DateTime.Now.TimeOfDay);
if (res != null){
Random random = new Random();
var randomValue = random.Next(res[0], res[1]);
}
If you have such specific conditions, the only way is to manually check, which of them applies.
private Random randomGenerator = new Random();
function decibel(DateTime d) {
int randMin = 0, randMax = 0; //the interval for the random value
var t = d.TimeOfDay;
if (t.TotalHours < 7) //0:00 - 6:59:59
{
randMin = 10; randMax = 20;
}
else if (d.TotalHours < 9) //7:00 - 8:59:59
{
randMin = 20; randMax = 40;
}
else if (d.TotalHours >= 11.5 && d.TotalHours < 13.5) //11:30 - 13:29:59
{
randMin = 60; randMax = 80;
}
...
// returns a rand with: randMin <= rand < randMax
return randomGenerator.Next(randMin, randMax);
}
How to get the type of input data from a range of numbers, combining it with an Enum in c#?
I want to avoid using the if/else, moving it to some kind of range, maybe with using IsDefined?
[Flags]
public enum PaymentCodeTypes
{
Other = 1,
Warranty = 2,
Contract = 4
}
var PaymentCodeType = 0;
if (paymentCode >= 80 && paymentCode <= 89)
{
PaymentCodeType = (int) PaymentCodeTypes.Contract;
}
else if (paymentCode >= 90 && paymentCode <= 99)
{
PaymentCodeType = (int) PaymentCodeTypes.Warranty;
} else
{
PaymentCodeType = (int)PaymentCodeTypes.Other;
}
You can use a switch expression, added in C# 8:
var PaymentCodeType = (int)(paymentCode switch {
var x when x >= 80 && x <= 89 => PaymentCodeTypes.Contract,
var x when x >= 90 && x <= 99 => PaymentCodeTypes.Warranty,
_ => PaymentCodeTypes.Other
});
If you want to make it even shorter, sacrificing maintainability:
var PaymentCodeType = (int)((paymentCode / 10) switch { // only works for ranges in the form of 10x ~ (10x + 9)
8 => PaymentCodeTypes.Contract,
9 => PaymentCodeTypes.Warranty,
_ => PaymentCodeTypes.Other
});
I think this is what you are looking for. You can change the ranges without changing code and would be more compliant with SOLID principles.
[Flags]
public enum PaymentCodeTypes
{
Other = 1,
Warranty = 2,
Contract = 4
}
private class Range {
public Range(int highEnd, int lowEnd, PaymentCodeTypes paymentCodeType)
{
this.highEnd = highEnd;
this.lowEnd = lowEnd;
this.paymentCodeType = paymentCodeType;
}
public int lowEnd { get; private set; }
public int highEnd { get; private set; }
public PaymentCodeTypes paymentCodeType { get; private set; }
}
private readonly List<Range> paymentCodeRanges = new List<Range>
{
new Range(0, 79, PaymentCodeTypes.Other),
new Range(80, 89, PaymentCodeTypes.Contract),
new Range(90, 99, PaymentCodeTypes.Warranty),
new Range(100, Int32.MaxValue, PaymentCodeTypes.Other )
};
public int SetPaymentTypeCode(int paymentCode)
{
var range = paymentCodeRanges.Where(x => paymentCode >= x.lowEnd && paymentCode <= x.highEnd).First();
return (int)range.paymentCodeType;
}
I Have a list that contains four item (A, B, C, D). Every item has a probability to be chosen. Let's say for example A has 74% of chance to be picked, B 15%, C 7% ,and D 4%.
I want to create a function that choose randomly an item according to its probability.
Any help please?
Define a class for your items like this:
class Items<T>
{
public double Probability { get; set; }
public T Item { get; set; }
}
then initialize it
var initial = new List<Items<string>>
{
new Items<string> {Probability = 74 / 100.0, Item = "A"},
new Items<string> {Probability = 15 / 100.0, Item = "B"},
new Items<string> {Probability = 7 / 100.0, Item = "C"},
new Items<string> {Probability = 4 / 100.0, Item = "D"},
};
then you need to convert it to aggregate a sum of probabilities from 0 to 1
var converted = new List<Items<string>>(initial.Count);
var sum = 0.0;
foreach (var item in initial.Take(initial.Count - 1))
{
sum += item.Probability;
converted.Add(new Items<string> {Probability = sum, Item = item.Item});
}
converted.Add(new Items<string> {Probability = 1.0, Item = initial.Last().Item});
now you can pick an item from converted collection with respect to probability:
var rnd = new Random();
while (true)
{
var probability = rnd.NextDouble();
var selected = converted.SkipWhile(i => i.Probability < probability).First();
Console.WriteLine($"Selected item = {selected.Item}");
}
NOTE: my implementation have O(n) complexity. You can optimize it with binary search (because values in converted collection are sorted)
My apologies for answering this one like this - I'm kinda viewing it as a sort of "Euler.Net" puzzle, and a way of playing around with Generics.
Anyway, here's my go at it:
public class WeightedItem<T>
{
private T value;
private int weight;
private int cumulativeSum;
private static Random rndInst = new Random();
public WeightedItem(T value, int weight)
{
this.value = value;
this.weight = weight;
}
public static T Choose(List<WeightedItem<T>> items)
{
int cumulSum = 0;
int cnt = items.Count();
for (int slot = 0; slot < cnt; slot++)
{
cumulSum += items[slot].weight;
items[slot].cumulativeSum = cumulSum;
}
double divSpot = rndInst.NextDouble() * cumulSum;
WeightedItem<T> chosen = items.FirstOrDefault(i => i.cumulativeSum >= divSpot);
if (chosen == null) throw new Exception("No item chosen - there seems to be a problem with the probability distribution.");
return chosen.value;
}
}
Usage:
WeightedItem<string> alice = new WeightedItem<string>("alice", 1);
WeightedItem<string> bob = new WeightedItem<string>("bob", 1);
WeightedItem<string> charlie = new WeightedItem<string>("charlie", 1);
WeightedItem<string> diana = new WeightedItem<string>("diana", 4);
WeightedItem<string> elaine = new WeightedItem<string>("elaine", 1);
List<WeightedItem<string>> myList = new List<WeightedItem<string>> { alice, bob, charlie, diana, elaine };
string chosen = WeightedItem<string>.Choose(myList);
using System;
public class Test{
private static String[] values = {"A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","B","B","B","B","B","B","B","B","B","B","B","B","B","B","C","C","C","C","C","C","C","D","D","D","D",};
private static Random PRNG = new Random();
public static void Main(){
Console.WriteLine( values[PRNG.Next(values.Length)] );
}
}
I'm trying to make a calendar using wpf. By using itemsPanel and more, I have a grid with 7 columns(sunday-saturday) and 6 rows(week# of month). If i can find the starting position of the first of each month by getting the weekday and week number(of the month), how can I find the week number(0-5 of each month)? Also can't I somehow just fill in the calendar from there? I'm lost and I don't know what else to try.
public partial class SchedulePage : Page
{
MainWindow _parentForm;
public int dayofweek;
public SchedulePage(MainWindow parentForm)
{
InitializeComponent();
_parentForm = parentForm;
// DateTime date = new DateTime(year, month, day);
_parentForm.bindings = new BindingCamper();
_parentForm.bindings.schedule.Add(new Schedule { WeekNo = (int) getWeekNumber(), WeekDay = dayofweek });
DataContext = _parentForm.bindings;
// lblTest.Content = dates(2011, 10, 27);
}
public double getWeekNumber()
{
dayofweek = getWeekDay(2011, 10, 31);
double h = dayofweek / 7;
double g = Math.Floor(h);
return g;
}
public int getWeekDay(int year, int month, int day)
{
//year = 2011;
//month = 10;
//day = 27;
int[] t = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
// year -= month < 3;
return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7;
}
You must use Calendar.GetDayOfWeek and Calendar.GetWeekOfYear in preference to writing yourself.
You can guarantee that if you write any date / time handling code yourself it will contain faults and won't work in different locales.
public class Row
{
public string MonthWeek { get; set; }
public string Year { get; set; }
public string Month { get; set; }
public string Day { get; set; }
public string WeekOfYear { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var l = new List<Row>();
DateTime startDate = DateTime.Now;
DateTime d = new DateTime(startDate.Year, startDate.Month, 1);
var cal = System.Globalization.DateTimeFormatInfo.CurrentInfo.Calendar;
var ms = cal.GetWeekOfYear(new DateTime(d.Year, d.Month, 1), System.Globalization.CalendarWeekRule.FirstDay, System.DayOfWeek.Sunday);
for (var i = 1; d.Month == startDate.Month; d = d.AddDays(1))
{
var si = new Row();
var month_week = (d.Day / 7) + 1;
si.MonthWeek = month_week.ToString();
si.Month = d.Year.ToString();
si.Year = d.Month.ToString();
si.Day = d.Day.ToString();
si.WeekOfYear = cal.GetWeekOfYear(d, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString();
l.Add(si);
}
dataGrid1.ItemsSource = l;
}
}
together with the obligatory DataGrid in the XAML:
<DataGrid AutoGenerateColumns="true" Name="dataGrid1" />
You can use Calendar.GetWeekOfYear from Globalization to do this.
Here's the MSDN docs for it: http://msdn.microsoft.com/en-us/library/system.globalization.calendar.getweekofyear.aspx
You should pass the appropriate culture properties from CultureInfo.CurrentCulture to GetWeekOfYear so that you match the current culture properly.
Example:
int GetWeekOfYear(DateTime date)
{
return Calendar.GetWeekOfYear(
date,
CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek
);
}
You could easily modify this into an extension method on DateTime:
static int GetWeekOfYear(this DateTime date)
{
return Calendar.GetWeekOfYear(
date,
CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek
);
}
With #Polynomial answer, I have this error:
An object reference is required for the non-static field, method, or property...
If you instanciate GregorianCalendar before then you can call the method GetWeekOfYear !
private static int GetWeekNumber(DateTime time)
{
GregorianCalendar cal = new GregorianCalendar();
int week = cal.GetWeekOfYear(time, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
return week;
}
This question already has answers here:
Is there a C# type for representing an integer Range?
(10 answers)
Closed 8 years ago.
I have a interval of number [1, 20].
I want a method which returns me range of number available if I decide to ban range [15, 18]. My method should return me a list containing [1,15] and [18, 20]
Range object could looks like something like that
public class Range
{
int Start {get;set;}
int End {get;set;}
}
Any help would be appreciated.
What about this?
IEnumerable<int> range = Enumerable.Range(1, 20);
IEnumerable<int> banned = Enumerable.Range(15, 4);
return range.Except(banned);
The Enumerable class already has a static method which will return a range of values for you - might be simpler to just use those semantics.
This is one of the ways:
static void Main(string[] args)
{
int[] allNumbers = Enumerable.Range(1, 20).ToArray();
GetNumbers(ref allNumbers, new int[] { 16, 17 });
}
private static void GetNumbers(ref int[] nums, int[]exclude)
{
List<int> numsToExlucde =new List<int>();
numsToExlucde.InsertRange(0, exclude);
nums = nums.Where(w => !numsToExlucde.Contains(w)).ToArray();
}
You have four possible cases. The method could look like this (I assume that range contain integer numbers only):
public class Range
{
public int Start { get; set; }
public int End { get; set; }
public IList<Range> Exclude(Range r)
{
if (r.Start <= Start && r.End < End)
{
return new List<Range>{new Range { Start = r.End + 1, End = End }};
}
else if (r.Start > Start && r.End >= End)
{
return new List<Range>{new Range { Start = Start, End = r.Start - 1 }};
}
else if (r.Start > Start && r.End < End)
{
return new List<Range>
{
new Range { Start = Start, End = r.Start - 1 },
new Range { Start = r.End + 1, End = End }
};
}
return new List<Range>();
}
}
// ...
static void Main(string[] args)
{
Range r = new Range { Start = 1, End = 20};
var list = r.Exclude(new Range{ Start = 1, End = 2} );
}
This can help you remove a range from another, or from a set of ranges:
public class Range {
public int Start { get; private set; }
public int End { get; private set; }
public Range(int start, int end) {
Start = start;
End = end;
}
public IEnumerable<Range> RemoveFrom(Range range) {
return RemoveFrom(new Range[] { range });
}
public IEnumerable<Range> RemoveFrom(IEnumerable<Range> items) {
foreach (Range item in items) {
if (End >= item.Start && Start <= item.End) {
if (item.Start <= Start) {
yield return new Range(item.Start, Start);
}
if (item.End >= End) {
yield return new Range(End, item.End);
}
} else {
yield return item;
}
}
}
}
Example:
Range range = new Range(1, 20);
foreach (Range r in new Range(15,18).RemoveFrom(range)) {
Console.WriteLine("{0} - {1}", r.Start, r.End);
}
Output:
1 - 15
18 - 20
Example of removing multiple ranges from other ranges:
Range[] items = new Range[] { new Range(1, 100), new Range(200, 300) };
Range[] exclude = new Range[] { new Range(25, 75), new Range(250, 280), new Range(90, 210) };
foreach (Range r in exclude) {
items = r.RemoveFrom(items).ToArray();
}
foreach (Range r in items) {
Console.WriteLine("{0} - {1}", r.Start, r.End);
}
Output:
1 - 25
75 - 90
210 - 250
280 - 300
You need to traverse through the banned ranges and iteratively create the valid ranges while doing so.
public List<Range> getValidRanges(Range total, List<Range> banned)
{
List<Range> valid = new ArrayList<Range>();
int start = total.getStartTime();
for(Range range: banned)
{
valid.add(new Range(start,banned.getStart()));
start = banned.getEnd();
}
valid.add(new Range(start,total.getEnd()));
return valid;
}