I want to write a code that every 25 times, the program should go back to run Part_1_code as follow. Here is problem that, Part_2_code has a lot of variables created in the Part_1_code, so following code is not allowed, but we couldn't not declare those variables at the beginning, since they are all anonymous type (read-only) so how to solve it? I do not wish to create a new class.
int Count = 1;
foreach (DateTime Date in TestDate)
{
if(Count == 1 || Count == 25){
Part_1_code;
Count = 1;
}
Pair_2_code;
Count++;
}
Variables which need to be accessible in a certain scope must also be defined in that scope, or a parent (outer) scope. Apart from that, it might be simpler to use a plain for loop in this case, combined with a module (%) operator.
It would also be slightly more precise to say you execute your "specific" code every 24 times, the way it's written. Using 1-based indexing in C-like languages is rather awkward.
for (var count = 0; count < TestDates.Count; count++)
{
var date = TestDates[count];
SomeStuff stuff = null;
if (count % 24 == 0)
{
// runs on 0, 24, 48, ...
stuff = new SomeStuff();
}
// runs on each iteration, 'stuff' will be non-null only if
// previous part was executed.
}
You can also extract these branches into methods, and pass the shared state as a parameter:
// decide whether this should be outside the loop
// (i.e. keep the state from previous iterations),
// or declared within the `for` loop
var state = new SharedState();
for (var count = 0; count < TestDates.Count; count++)
{
var date = TestDates[count];
if (count % 24 == 0)
{
// runs on 0, 24, 48, ...
ProcessPart1(state);
}
// runs on each iteration
ProcessPart2(state);
}
Where SharedState contains any data you need to share between these branches:
class SharedState
{
public int SomeValue { get; set; }
public string SomeOtherValue { get; set; }
}
If your ProcessPart1 method returns a value, you can even use a ternary operator to simplify the code:
for (var count = 0; count < TestDates.Count; count++)
{
var date = TestDates[count];
// runs on 0, 24, 48, ...
var someData = (count % 24 == 0) ? Process1(date) : null;
// runs on each iteration
Process2(someData);
}
One option is to make state a private field in the parent class, but a common rule of thumb is to try to limit scope of your variables as much as possible.
Of course, if you posted some actual code which executes in these two branches, it could perhaps be possible to refactor the code slightly better.
This seems pretty straightforward, Part_1_Code can just be a function which returns your data.
int Count = 1;
Part1Result p1 = null;
foreach (DateTime Date in TestDate)
{
if(Count == 1 or Count == 25){
p1 = Part_1_code();
Count = 1;
}
//Part_2_code
//use variable p1 here - its the results from Part1Code
Count++;
}
elsewhere
public Part1Result Part_1_code()
{
return new Part1Result(){ SomeData = "foo" };
}
Consider what happens if you're if doesn't run
Then all the variables inside would be null.
You can't use variables out of scope.
You could create variables before if
and add the value in your if
int Count = 1;
foreach (DateTime Date in TestDate)
{
int someVariable;
if(Count == 1 or Count == 25){
Part_1_code that set the somevariable value;
Count = 1;
}
Pair_2_code that can access someVariable;
Count++;
}
You can initialize an anonymous type by using a dummy value:
var Count = 1;
var someAnonymous = new {Date = new DateTime(), Count = 0}; // init with dummy values
foreach (DateTime Date in TestDate)
{
if(Count == 1 || Count == 25){
// Part_1_code
Count = 1;
}
// Part_2_code
Count++;
}
Anonymous types are read-only, you cannot change the values of their properties, but you can replace the whole variable by assigning it again without changing any property.
Related
Input:
public class MyObject
{
public double Value { get; set; }
public DateTime Date { get; set; }
}
Method to generate test objects:
public static MyObject[] GetTestObjects()
{
var rnd = new Random();
var date = new DateTime(2021, 1, 1, 0, 0, 0);
var result = new List<MyObject>();
for (int i = 0; i < 50000; i++)
{
//this is to simulate real data having gaps
if (rnd.Next(100) < 25)
{
continue;
}
var myObject = new MyObject()
{
Value = rnd.NextDouble(),
Date = date.AddMinutes(15 * i)
};
result.Add(myObject);
}
return result.ToArray();
}
Given this I require to calculate maximum Value for previous 12 month for each myObject. I could just think of doing this InParallel, but maybe there is an optimized solution?
Sorry for being unclear, this is what I use right now to get what I want:
public MyObject[] BruteForceBackward(MyObject[] testData)
{
return testData.AsParallel().Select(point =>
{
var max = testData.Where(x => x.Date <= point.Date && x.Date >= point.Date.AddYears(-1)).Max(x => x.Value);
return new MyObject() { Date = point.Date, Value = point.Value / max };
}).OrderBy(r => r.Date).ToArray();
}
This works but it is slow and eats processor resources (imagine, you have 100k objects), I believe there must be something better
I had a simillar project where i had to calculate such stuff on tons of sensor data.
You can now find a little more refined version in my Github repository, which should be ready to use (.Net):
https://github.com/forReason/Statistics-Helper-Library
In general you want to reduce the amount of loops going over all your data. At best, you want to touch each element only one single time.
Process Array (equiv. of BruteForceBackwards)
public static MyObject[] FlowThroughForward(ref MyObject[] testData)
{
// generate return array
MyObject[] returnData = new MyObject[testData.Length];
// keep track to minimize processing
double currentMaximum = 0;
List<MyObject> maximumValues = new List<MyObject>();
// go through the elements
for (int i = 0; i < testData.Length; i++)
{
// calculate the oldest date to keep in tracking list
DateTime targetDate = testData[i].Date.AddYears(-1);
// maximum logic
if (testData[i].Value >= currentMaximum)
{
// new maximum found, clear tracking list
// this is the best case scenario
maximumValues.Clear();
currentMaximum = testData[i].Value;
}
else
{
// unfortunately, no new maximum was found
// go backwards the maximum tracking list and check for smaller values
// clear the list of all smaller values. The list should therefore always
// be in descending order
for (int b = maximumValues.Count - 1; b >= 0; b--)
{
if (maximumValues[b].Value <= testData[i].Value)
{
// a lower value has been found. We have a newer, higher value
// clear this waste value from the tracking list
maximumValues.RemoveAt(b);
}
else
{
// there are no more lower values.
// stop looking for smaller values to save time
break;
}
}
}
// append new value to tracking list, no matter if higher or lower
// all future values might be lower
maximumValues.Add(testData[i]);
// check if the oldest value is too old to be kept in the tracking list
while (maximumValues[0].Date < targetDate)
{
// oldest value is to be removed
maximumValues.RemoveAt(0);
// update maximum
currentMaximum = maximumValues[0].Value;
}
// add object to result list
returnData[i] = new MyObject() { Date = testData[i].Date, Value = testData[i].Value / currentMaximum }; ;
}
return returnData;
}
Real Time Data or Streamed Data
Note: If you have really large lists, you might get memory issues with your approach to pass a full array. In this case: pass one value at a time, pass them from oldest value to newest value. Store the values back one at a time.
This Function can also be used on real time data.
The test method is included in code.
static void Main(string[] args)
{
int length = 50000;
Stopwatch stopWatch1 = new Stopwatch();
stopWatch1.Start();
var myObject = new MyObject();
var result = new List<MyObject>();
var date = new DateTime(2021, 1, 1, 0, 0, 0);
for (int i = 0; i < length; i++)
{
//this is to simulate real data having gaps
if (rnd.Next(100) < 25)
{
continue;
}
myObject.Value = rnd.NextDouble();
myObject.Date = date.AddMinutes(15 * i);
result.Add(CalculateNextObject(ref myObject));
}
stopWatch1.Stop();
Console.WriteLine("test code executed in " + stopWatch1.ElapsedMilliseconds + " ms");
Thread.Sleep(1000000);
}
private static Random rnd = new Random();
private static double currentMaximum = 0;
private static List<MyObject> maximumValues = new List<MyObject>();
public static MyObject CalculateNextObject(ref MyObject input)
{
// calculate the oldest date to keep in tracking list
DateTime targetDate = input.Date.AddYears(-1);
// maximum logic
if (input.Value >= currentMaximum)
{
// new maximum found, clear tracking list
// this is the best case scenario
maximumValues.Clear();
currentMaximum = input.Value;
}
else
{
// unfortunately, no new maximum was found
// go backwards the maximum tracking list and check for smaller values
// clear the list of all smaller values. The list should therefore always
// be in descending order
for (int b = maximumValues.Count - 1; b >= 0; b--)
{
if (maximumValues[b].Value <= input.Value)
{
// a lower value has been found. We have a newer, higher value
// clear this waste value from the tracking list
maximumValues.RemoveAt(b);
}
else
{
// there are no more lower values.
// stop looking for smaller values to save time
break;
}
}
}
// append new value to tracking list, no matter if higher or lower
// all future values might be lower
maximumValues.Add(input);
// check if the oldest value is too old to be kept in the tracking list
while (maximumValues[0].Date < targetDate)
{
// oldest value is to be removed
maximumValues.RemoveAt(0);
// update maximum
currentMaximum = maximumValues[0].Value;
}
// add object to result list
MyObject returnData = new MyObject() { Date = input.Date, Value = input.Value / currentMaximum };
return returnData;
}
Test Method
static void Main(string[] args)
{
MyObject[] testData = GetTestObjects();
Stopwatch stopWatch1 = new Stopwatch();
Stopwatch stopWatch2 = new Stopwatch();
stopWatch1.Start();
MyObject[] testresults1 = BruteForceBackward(testData);
stopWatch1.Stop();
Console.WriteLine("BruteForceBackward executed in " + stopWatch1.ElapsedMilliseconds + " ms");
stopWatch2.Start();
MyObject[] testresults2 = FlowThroughForward(ref testData);
stopWatch2.Stop();
Console.WriteLine("FlowThroughForward executed in " + stopWatch2.ElapsedMilliseconds + " ms");
Console.WriteLine();
Console.WriteLine("Comparing some random test results: ");
var rnd = new Random();
for (int i = 0; i < 10; i++)
{
int index = rnd.Next(0, testData.Length);
Console.WriteLine("Index: " + index + " brute: " + testresults1[index].Value + " flow: " + testresults2[index].Value);
}
Thread.Sleep(1000000);
}
Test result
Tests were performed on a machine with 32 cores, so in teory multithreaded aproach should be at advantage but youll see ;)
Function
Function Time
time %
BruteForceBackward
5334 ms
99.9%
FlowThroughForward
5 ms
0.094%
Performance improvement factor: ~time/1000
console output with data validation:
BruteForceBackward executed in 5264 ms
FlowThroughForward executed in 5 ms
Comparing some random test results:
Index: 25291 brute: 0.989688139105413 flow: 0.989688139105413
Index: 11945 brute: 0.59670821976193 flow: 0.59670821976193
Index: 30282 brute: 0.413238225210297 flow: 0.413238225210297
Index: 33898 brute: 0.38258761939139 flow: 0.38258761939139
Index: 8824 brute: 0.833512217105447 flow: 0.833512217105447
Index: 22092 brute: 0.648052464067263 flow: 0.648052464067263
Index: 24633 brute: 0.35859417692481 flow: 0.35859417692481
Index: 24061 brute: 0.540642018793402 flow: 0.540642018793402
Index: 34219 brute: 0.498785766613022 flow: 0.498785766613022
Index: 2396 brute: 0.151471808392111 flow: 0.151471808392111
Cpu usage was a lot higher on Bruteforce backwards due to parallelisation.
The worst case scenario are long periods of decreasing values. The code can still be vastly optimized but I guess this should be sufficient. For further optimisation, one might look to reduce the list shuffles when removing/adding elements to maximumValues.
An interesting and challenging problem. I put together a solution using a dynamic programming approach (first learned back in CS algorithms class back in '78). First, a tree is constructed containing pre-calculated local max values over recursively defined ranges. Once constructed, the max value for an arbitrary range can be efficiently calculated mostly using the pre-calculated values. Only at the fringes of the range does the calculation drop down to the element level.
It is not as fast as julian bechtold's FlowThroughForward method, but random access to ranges may be a plus.
Code to add to Main:
Console.WriteLine();
Stopwatch stopWatch3 = new Stopwatch();
stopWatch3.Start();
MyObject[] testresults3 = RangeTreeCalculation(ref testData, 10);
stopWatch3.Stop();
Console.WriteLine($"RangeTreeCalculation executed in {stopWatch3.ElapsedMilliseconds} ms");
... test comparison
Console.WriteLine($"Index: {index} brute: {testresults1[index].Value} flow: {testresults2[index].Value} rangeTree: {testresults3[index].Value}");
Test function:
public static MyObject[] RangeTreeCalculation(ref MyObject[] testDataArray, int partitionThreshold)
{
// For this implementation, we need to convert the Array to an ArrayList, because we need a
// reference type object that can be shared.
List<MyObject> testDataList = testDataArray.ToList();
// Construct a tree containing recursive collections of pre-calculated values
var rangeTree = new RangeTree(testDataList, partitionThreshold);
MyObject[] result = new MyObject[testDataList.Count];
Parallel.ForEach(testDataList, (item, state, i) =>
{
var max = rangeTree.MaxForDateRange(item.Date.AddYears(-1), item.Date);
result[i] = new MyObject() { Date = item.Date, Value = item.Value / max };
});
return result;
}
Supporting class:
// Class used to divide and conquer using dynamic programming.
public class RangeTree
{
public List<MyObject> Data; // This reference is shared by all members of the tree
public int Start { get; } // Index of first element covered by this node.
public int Count { get; } // Number of elements covered by this node.
public DateTime FirstDateTime { get; }
public DateTime LastDateTime { get; }
public double MaxValue { get; } // Pre-calculated max for all elements covered by this node.
List<RangeTree> ChildRanges { get; }
// Top level node constructor
public RangeTree(List<MyObject> data, int partitionThreshold)
: this(data, 0, data.Count, partitionThreshold)
{
}
// Child node constructor, which covers an recursively decreasing range of element.
public RangeTree(List<MyObject> data, int start, int count, int partitionThreshold)
{
Data = data;
Start = start;
Count = count;
FirstDateTime = Data[Start].Date;
LastDateTime = Data[Start + Count - 1].Date;
if (count <= partitionThreshold)
{
// If the range is smaller than the threshold, just calculate the local max
// directly from the items. No child ranges are defined.
MaxValue = Enumerable.Range(Start, Count).Select(i => Data[i].Value).Max();
}
else
{
// We still have a significant range. Decide how to further divide them up into sub-ranges.
// (There may be room for improvement here to better balance the tree.)
int partitionSize = (count - 1) / partitionThreshold + 1;
int partitionCount = (count - 1) / partitionSize + 1;
if (count < partitionThreshold * partitionThreshold)
{
// When one away from leaf nodes, prefer fewer full leaf nodes over more
// less populated leaf nodes.
partitionCount = (count - 1) / partitionThreshold + 1;
partitionSize = (count - 1) / partitionCount + 1;
}
ChildRanges = Enumerable.Range(0, partitionCount)
.Select(partitionNum => new {
ChildStart = Start + partitionNum * partitionSize,
ChildCount = Math.Min(partitionSize, Count - partitionNum * partitionSize)
})
.Where(part => part.ChildCount > 0) // Defensive
.Select(part => new RangeTree(Data, part.ChildStart, part.ChildCount, partitionThreshold))
.ToList();
// Now is the dynamic programming part:
// Calculate the local max as the max of all child max values.
MaxValue = ChildRanges.Max(chile => chile.MaxValue);
}
}
// Get the max value for a given range of dates withing this rangeTree node.
// This used the precalculated values as much as possible.
// Only at the fringes of the date range to we calculate at the element level.
public double MaxForDateRange(DateTime fromDate, DateTime thruDate)
{
double calculatedMax = Double.MinValue;
if (fromDate > this.LastDateTime || thruDate < this.FirstDateTime)
{
// Entire range is excluded. Nothing of interest here folks.
calculatedMax = Double.MinValue;
}
else if (fromDate <= this.FirstDateTime && thruDate >= this.LastDateTime)
{
// Entire range is included. Use the already-calculated max.
calculatedMax = this.MaxValue;
}
else if (ChildRanges != null)
{
// We have child ranges. Recurse and accumulate.
// Possible optimization: Calculate max for middle ranges first, and only bother
// with extreme partial ranges if their local max values exceed the preliminary result.
for (int i = 0; i < ChildRanges.Count; ++i)
{
double childMax = ChildRanges[i].MaxForDateRange(fromDate, thruDate);
if (childMax > calculatedMax)
{
calculatedMax = childMax;
}
}
}
else
{
// Leaf range. Loop through just this limited range of notes, checking individually for
// date in range and accumulating the result.
for (int i = 0; i < this.Count; ++i)
{
var element = Data[this.Start + i];
if (fromDate <= element.Date && element.Date <= thruDate && element.Value > calculatedMax)
{
calculatedMax = element.Value;
}
}
}
return calculatedMax;
}
}
There's plenty of room for improvement, such as parameterizing the types and generalizing the functionality to support more than just Max(Value), but the framework is there.
Assuming you meant you need the maximum Value for each of the last 12 months from result, then you can use LINQ:
var beginDateTime = DateTime.Now.AddMonths(-12);
var ans = result.Where(r => r.Date >= beginDateTime).GroupBy(r => r.Date.Month).Select(mg => mg.MaxBy(r => r.Value)).ToList();
Running some timing, I get that putting AsParallel after result changes the run time from around 16ms (first run) to around 32ms, so it is actually slower. It is about the same after the Where and about 23ms after the GroupBy (processing the 12 groups in parallel). On my PC at least, there isn't enough data or complex operations for parallelism, but the GroupBy isn't the most efficient.
Using an array and testing each element, I get the results in about 1.2ms:
var maxMOs = new MyObject[12];
foreach (var r in result.Where(r => r.Date >= beginDateTime)) {
var monthIndex = r.Date.Month-1;
if (maxMOs[monthIndex] == null || r.Value > maxMOs[monthIndex].Value)
maxMOs[monthIndex] = r;
}
Note that the results are not chronological; you could offset monthIndex by today's month to order the results if desired.
var maxMOs = new MyObject[12];
var offset = DateTime.Now.Month-11;
foreach (var r in result.Where(r => r.Date >= beginDateTime)) {
var monthIndex = r.Date.Month-offset;
if (maxMOs[monthIndex] == null || r.Value > maxMOs[monthIndex].Value)
maxMOs[monthIndex] = r;
}
A micro-optimization (mostly useful on repeat runnings) is to invert the test and use the null-propagating operator:
if (!(r.Value <= maxMOs[monthIndex]?.Value))
This saves about 0.2ms on the first run but up to 0.5ms on subsequent runs.
Here is a solution similar to julian bechtold's answer. Difference is that the maximum (and all related variables) are kept hidden away from the main implementation, in a separate class whose purpose is solely to keep track of the maximum over the past year. Algorithm is the same, I just use a few Linq expressions here and there.
We keep track of the maximum in the following class:
public class MaxSlidingWindow
{
private readonly List<MyObject> _maximumValues;
private double _max;
public MaxSlidingWindow()
{
_maximumValues = new List<MyObject>();
_max = double.NegativeInfinity;
}
public double Max => _max;
public void Add(MyObject myObject)
{
if (myObject.Value >= _max)
{
_maximumValues.Clear();
_max = myObject.Value;
}
else
{
RemoveValuesSmallerThan(myObject.Value);
}
_maximumValues.Add(myObject);
RemoveObservationsBefore(myObject.Date.AddYears(-1));
_max = _maximumValues[0].Value;
}
private void RemoveObservationsBefore(DateTime targetDate)
{
var toRemoveFromFront = 0;
while (_maximumValues[toRemoveFromFront].Date < targetDate && toRemoveFromFront <= maximumValues3.Count -1)
{
toRemoveFromFront++;
}
_maximumValues.RemoveRange(0, toRemoveFromFront);
}
private void RemoveValuesSmallerThan(double targetValue)
{
var maxEntry = _maximumValues.Count - 1;
var toRemoveFromBack = 0;
while (toRemoveFromBack <= maxEntry && _maximumValues[maxEntry - toRemoveFromBack].Value <= targetValue)
{
toRemoveFromBack++;
}
_maximumValues.RemoveRange(maxEntry - toRemoveFromBack + 1, toRemoveFromBack);
}
}
It can be used as follows:
public static MyObject[] GetTestObjects_MaxSlidingWindow()
{
var rnd = new Random();
var date = new DateTime(2021, 1, 1, 0, 0, 0);
var result = new List<MyObject>();
var maxSlidingWindow = new MaxSlidingWindow();
for (int i = 0; i < 50000; i++)
{
//this is to simulate real data having gaps
if (rnd.Next(100) < 25)
{
continue;
}
var myObject = new MyObject()
{
Value = rnd.NextDouble(),
Date = date.AddMinutes(15 * i)
};
maxSlidingWindow.Add(myObject);
var max = maxSlidingWindow.Max;
result.Add(new MyObject { Date = myObject.Date, Value = myObject.Value / max });
}
return result.ToArray();
}
See the relative timings below - above solution is slightly faster (timed over 10 million runs), but barely noticeable:
Relative timings
Hello I have an array of enumerations, and I'm trying sort that array by their enumeration value and get the array index's of the top
private enum Values
{
LOW,
MEDIUM,
HIGH
}
private Values[] Settings = new Values[10];
Settings[0] = LOW;
Settings[1] = HIGH;
Settings[2] = MEDIUM;
Settings[3] = LOW;
Settings[4] = LOW;
Settings[5] = LOW;
Settings[6] = LOW;
Settings[7] = LOW;
Settings[8] = MEDIUM;
Settings[9] = MEDIUM;
Basically now, with what I have above, I need to sort the settings array by enumeration value and get the array indexes of the top (let's say 3) items;
So I'd get back values 1, 2, 8
The platform I'm using does not support LINQ so those handy functions are not available.
I've been trying to wrap my head around this but it would help to have another pair of eyes.
thanks.
Implement a wrapper reference type,
class ValueWrapper : IComparable<ValueWrapper>
{
public Values Value { get; set; }
public int Index { get; set; }
public int CompareTo(ValueWrapper other)
{
return this.Value.CompareTo(other.Value) * -1; // Negating since you want reversed order
}
}
Usage -
ValueWrapper[] WrappedSettings = new ValueWrapper[10];
for(int i = 0; i < WrappedSettings.Length; i++)
{
WrappedSettings[i] = new ValueWrapper { Value = Settings[i], Index = i };
}
Array.Sort(WrappedSettings);
WrappedSettings will be sorted as you specified, preserving the indexes they were in the original array.
how about this:
Values first = Values.Low,second = Values.Low,third = Values.Low;
int firstI = -1,secondI = -1, thirdI = -1;
for(int i = 0;i < Settings.Length;i++)
{
if(Settings[i] > first || firstI == -1)
{
third = second;
thirdI = secondI;
second= first;
secondI= firstI;
first = Settings[i];
firstI = i;
}
else if(Settings[i] > second || secondI == -1)
{
third = second;
thirdI = secondI;
second = Settings[i];
secondI = i;
}
else if(Settings[i] > third || thirdI == -1)
{
third = Settings[i];
thirdI = i;
}
}
So, since you say you work in an environment where Linq is not available, I assume that other things like generics, nullables and so on will not be available either. A very low-tech solution.
Basic idea:
For every possible enum value, from highest to lowest, go through the list. If we find that value, output it and remember how many we have output. If we reach 3, stop the algorithm.
So, we first look for HIGH in the list, then for MEDIUM and so on.
class Program
{
private enum Values
{
LOW,
MEDIUM,
HIGH
}
static void Main(string[] args)
{
// Sample data
Values[] settings = new Values[10];
settings[0] = Values.LOW;
settings[1] = Values.HIGH;
settings[2] = Values.MEDIUM;
settings[3] = Values.LOW;
settings[4] = Values.LOW;
settings[5] = Values.LOW;
settings[6] = Values.LOW;
settings[7] = Values.LOW;
settings[8] = Values.MEDIUM;
settings[9] = Values.MEDIUM;
// Get Values of the enum type
// This list is sorted ascending by value but may contain duplicates
Array enumValues = Enum.GetValues(typeof(Values));
// Number of results found so far
int numberFound = 0;
// The enum value we used during the last outer loop, so
// we skip duplicate enum values
int lastValue = -1;
// For each enum value starting with the highest to the lowest
for (int i= enumValues.Length -1; i >= 0; i--)
{
// Get this enum value
int enumValue = (int)enumValues.GetValue(i);
// Check whether we had the same value in the previous loop
// If yes, skip it.
if(enumValue == lastValue)
{
continue;
}
lastValue = enumValue;
// For each entry in the list where we are searching
for(int j=0; j< settings.Length; j++)
{
// Check to see whether it is the currently searched value
if (enumValue == (int)settings[j])
{
// if yes, then output it.
Console.WriteLine(j);
numberFound++;
// Stop after 3 found entries
if (numberFound == 3)
{
goto finished;
}
}
}
}
finished:
Console.ReadLine();
}
}
Output is as requested 1,2,8
I'm not sure if this is exactly what you want because it doesn't sort the original array, but one way to get the indexes of the top three values is to simply store the indexes of the top values in another array. Then we can loop through the original array, and for each item, see if it's larger than any of the items at the indexes we've stored so far. If it is, then swap it with that item.
For example:
// Start the topIndexes array with all invalid indexes
var topIndexes = new[] {-1, -1, -1};
for (var settingIndex = 0; settingIndex < Settings.Length; settingIndex++)
{
var setting = Settings[settingIndex];
var topIndexLessThanSetting = -1;
// Find the smallest topIndex setting that's less than this setting
for (int topIndex = 0; topIndex < topIndexes.Length; topIndex++)
{
if (topIndexes[topIndex] == -1)
{
topIndexLessThanSetting = topIndex;
break;
}
if (setting <= Settings[topIndexes[topIndex]]) continue;
if (topIndexLessThanSetting == -1 ||
Settings[topIndexes[topIndex]] < Settings[topIndexes[topIndexLessThanSetting]])
{
topIndexLessThanSetting = topIndex;
}
}
topIndexes[topIndexLessThanSetting] = settingIndex;
}
// topIndexes = { 1, 2, 8 }
I am looking for optimization for the following.
I have an object that has a few properties. For simplicity i will show the only 2 that matter in this scenario.
public class DomainData
{
public long Id{ get; set; }
public long SoldTickets { get; set; }
}
I make a call to my db and get a list of the above object:
var databaseData = _iOvervatchManager.GetDomainData(id);// gets date from db
var model = new List<DomainModel>();
Now I need to calculate how many tickets have been sold between list items.
current = current - previous
I do the calculation with the following code:
for (int i = 0; i <= databaseData.Count() -1; i++)
{
if (i != 0)
{
long ticket = databaseData[i].SoldTickets- databaseData[i - 1].SoldTickets;
model.Add(new DomainModel
{
DomainId = databaseData[i].DomainId ,
SoldTicketsLastUpdate = ticket
});
}
}
And now set the value on the original object.
for(int i= 0; i< databaseData.Count(); i++)
{
if(databaseData[i].Id == model[i].DomainId )
databaseData[i].SoldTickets = model[i].SoldTicketsLastUpdate;
}
I consider this an awful way of accomplishing this since I recon I can do this in the first loop but I can't figure out how.
my first attempt was :
for (int i = 0; i <= databaseData.Count() -1; i++)
{
if (i != 0)
{
databaseData[i].SoldTickets
= databaseData[i].SoldTickets - databaseData[i - 1].SoldTickets;
}
}
But this was wrong since i change the object value and when I come to the next increment the previous value has been modified so the current for that loop increment gets a wrong calculation.
How can I optimize this?
Assuming that no new data will be added after this runs (!!), execute the for-loop backwards:
for (int i = databaseData.Count() - 1; i > 0; i--)
{
databaseData[i].SoldTickets = databaseData[i].SoldTickets - databaseData[i - 1].SoldTickets;
}
And because the loop only runs for i > 0, the check for i != 0 is unneeded.
I am searching of an elegant way to assign values in function of a number belonging to a specific range.
For example, having the number X, the elegant way would return:
'a' - if X is between 0 and 1000
'b' - if X is between 1000 and 1500
and so on (but a fixed number of defined intervals)
By elegant I mean something more appealing than
if ((x => interval_1) && (x < interval_2))
class_of_x = 'a';
else if ((x => interval_2) && (x < interval_3))
class_of_x = 'b';
...
or
if(Enumerable.Range(interval_1, interval_2).Contains(x))
class_of_x = 'a';
else if(Enumerable.Range(interval_2 + 1, interval_3).Contains(x))
class_of_x = 'b';
...
I hate seeing so many IFs.
Also, the interval values can be stored in a collection (maybe this would help me eliminate the ISs?), not necessary as interval_1, interval_2 and so on.
Somewhat inspired by this question How to elegantly check if a number is within a range? which came out while looking for a solution for the problem described above.
You can create extention method:
public static class IntExtensions
{
// min inclusive, max exclusive
public static bool IsBetween(this int source, int min, int max)
{
return source >= min && source < max
}
}
and then
// Item1 = min, Item2 = max, Item3 = character class
IList<Tuple<int, int, char>> ranges = new List<Tuple<int, int, char>>();
// init your ranges here
int num = 1;
// assuming that there certainly is a range which fits num,
// otherwise use "OrDefault"
// it may be good to create wrapper for Tuple,
// or create separate class for your data
char characterClass = ranges.
First(i => num.IsBetween(i.Item1, i.Item2)).Item3;
If my comment is correct then your first if statement has a lot of unnecessary checks, if its not less than interval 2 then it must be greater than or equal to, therefore:
if((x => i1) && (x < i2))
else if(x < i3)
else if(x < i4)...
When a "true" argument is found then the rest of the if statement is irrelevant, as long as your conditions are in order this should suit your needs
Create an Interval class and use LINQ:
public class Interval
{
public string TheValue { get; set; }
public int Start { get; set; }
public int End { get; set; }
public bool InRange(int x)
{
return x >= this.Start && x <= this.End;
}
}
public void MyMethod()
{
var intervals = new List<Interval>();
// Add them here...
var x = 3213;
var correctOne = intervals.FirstOrDefault(i => i.InRange(x));
Console.WriteLine(correctOne.TheValue);
}
Firstly, define a little class to hold the inclusive maximum value, and the corresponding value to use for that band:
sealed class Band
{
public int InclusiveMax;
public char Value;
}
Then declare an array of Band which specifies the value to use for each band and loop to find the corresponding band value for any input:
public char GetSetting(int input)
{
var bands = new[]
{
new Band {InclusiveMax = 1000, Value = 'a'},
new Band {InclusiveMax = 1500, Value = 'b'},
new Band {InclusiveMax = 3000, Value = 'c'}
};
char maxSetting = 'd';
foreach (var band in bands)
if (input <= band.InclusiveMax)
return band.Value;
return maxSetting;
}
Note: In real code, you would wrap all this into a class which initialises the bands array only once, and not every single time it's called (as it is in the code above).
Here you could also use the static System.Linq.Enumerable's Range() method that implements
IEnumerable<T>
with the Contains() method (again from System.Linq.Enumerable), to do something like:
var num = 254;
if(Enumerable.Range(100,300).Contains(num)) { ...your logic here; }
This looks more elegant in my eyes at least.
I have the following foreach loop in C#:
foreach(var item in mod)
{
int i;
i = i + 1;
if (i % 2 == 0)
{
string y = "even number";
}
}
How come I get the message that local variable is unassigned. I am trying to find the even number here.
To resolve the message you are getting, you simply need to initialize it:
int i = 0;
If your intention is to count every item, then you will also need to change the scope of i to outside of the foreach. Otherwise, as you originally posted, the variable i will have the same value for every iteration of the loop.
See this code snippet for both the initialization and scope change:
int i = 0;
foreach (var item in mod)
{
i = i + 1; // is the first item considered even or odd? that answer changes where this should go
if (i % 2 == 0) {
string y = "even number";
}
}
You need to initialise your i variable:
int i = 0;
You're currently trying + 1 to an unassigned variable.
You get the warning because you are not assigning i before using it in i = i + 1. You want to declare i outside of your foreach loop, so you it isn't bound to the scope of the loop. Then initialize with 0 and use the increment feature. Something like:
int i = 0;
foreach (var item in mod)
{
i++;
if (i % 2 == 0)
{
string y = "even number";
}
}
An alternative to what others have suggested here (although correct) would be to use a for loop, this would take care of i for you, e.g.
for (int i = 0; i < mod.length; i++)
{
if (i % 2 == 0)
{
string y = "even number";
}
}
Change this: int i;
to this: int i = 0;
Values types in C# like int do have default values, but you're still not allowed to use an unassigned value-type variable.
You need to assign an initial value for i before you can do i=i+1
You have to initialize i. Right now the compiler is reading this as i = garbage in memory. So you have garbage in memory = garbage in memory + 1. That i could be equal to a string, a number, or anything.
int i = 0;
In addition, you need to initialize that variable outside of the for...each loop, or it will keep resetting itself to 0.
int i = 0;
foreach(var item in mod){
i = i + 1;
if (i % 2 == 0) {
string y = "even number";
}
}
int i = 0;
foreach(var item in mod)
{
if (i % 2 == 0)
{
string y = "even number";
}
i = i + 1;
}
//what are you doing with y and how are you returning y if you need it..
are you expecting to break out at some point.. ? what if item has zero items..??