I use this method called SearchConsequences to iterate through List<ValuesEO> of objects and perform some tasks, for getting values of particular fields, according to applied rules. I want to somehow simplify this code.
I want to switch (replace) everywhere in code the expression ValuesEO[i].powerR for other ValuesEO[i].otherField in the whole block of code.
At this time I do this just by block coping and changing it manually. So lets say, at the end, I have 5 blocks of really similar code blocks in this method. The only difference is in ValuesEO[i].otherField than ValuesEO[i].otherField2 ValuesEO[i].otherField3 ... and so on.
I don't like that block coping.
public Dictionary<Consequence,Cause> SearchConsequences(List<ResultsCatcher> smallTable, int n, ConnectHYSYS obj, int keyP, int keyR)//for one stream for one parameter
{
double threshold = 0.005;
Dictionary<Consequence,Cause> collection = new Dictionary<Consequence,Cause>();
//search in ValesE for each energy stream, for powerR
for (int i = 0; i < smallTable[n].ValuesE.Count; i++)
{
//sort the smallTable
smallTable.Sort((x, y) => x.ValuesE[i].powerR.CompareTo(y.ValuesE[i].powerR));
//get the index of first occurrence of powerR >= threshold, if there is nothing bigger than threshold, index is null
var tagged = smallTable.Select((item, ii) => new { Item = item, Index = (int?)ii });
int? index = (from pair in tagged
where pair.Item.ValuesE[i].powerR >= threshold
select pair.Index).FirstOrDefault();
//get needed information
if (index != null)
{
int id = Convert.ToInt16(index);
double newValue = smallTable[id].ValuesE[i].power;
double newValueR = smallTable[id].ValuesE[i].powerR;
TypeOfValue kindOf = TypeOfValue.power;
Consequence oneConsequence = new Consequence(obj.EnergyStreamsList[i], newValue, newValueR, kindOf);
Cause oneCause = new Cause();
oneCause.GetTableHeader(smallTable[id]);
collection.Add(oneConsequence,oneCause);
}
}
}
Maybe it is easy to accomplish that and somewhere this problem is discussed.
But I really even don't know how to google it.
This is a ready made program for you that demonstrates how to move your criteria/property selection outside your examination function. Have a look to see how fields criterias are matched.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
namespace Test
{
class Program
{
public class PowerValues
{
public double power;
public double powerR;
public double lightbulbs;
public double lightbulbsR;
}
public static void DoSomething(IEnumerable<PowerValues> powerValues, Func<PowerValues, double> criteria, double treshhold)
{
var flaggedElements = powerValues.Where(e => criteria(e) > treshhold);
foreach (var flagged in flaggedElements)
{
Console.WriteLine("Value flagged: {0}", criteria(flagged));
}
}
public static void Main(string[] args)
{
List<PowerValues> powerValues = new List<PowerValues>();
powerValues.Add(new PowerValues(){power=10, powerR=0.002, lightbulbs = 2, lightbulbsR = 2.006});
powerValues.Add(new PowerValues(){power=5, powerR=0.004, lightbulbs = 4, lightbulbsR = 2.09});
powerValues.Add(new PowerValues(){power=6, powerR=0.003, lightbulbs = 3, lightbulbsR = 2.016});
Console.WriteLine("Power matching criteria . . . ");
DoSomething(powerValues, (e) => e.powerR, 0.003);
Console.WriteLine("Lightbulbs matching criteria . . . ");
DoSomething(powerValues, (e) => e.lightbulbs, 3);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
Extract the code your using twice into one method.
Create an enum for the values you want (e.g. PowerR, OtherField)
Add the enum as a parameter to your method
Add a switch statement in your method to the places where the code changes
Related
I have code from a maths library i am using, and cannot change. The function in question from the maths library I am using uses an approximation function iteratively, like this.
public double Calculate(double startInput, Func<double, double> approximationFunc)
{
var result = 0d;
var max = 10;
for (int i = 0; i < max; i++)
{
result = approximationFunc(startInput);
//do some checks ...
startInput =DoSomething();
}
return result;
}
However, in the approximationFunc I am using 'in reality', I have to compute other things than the double result, and I need to re-use these results. The only thing I have come up with is:
public void BusinessLogic(double startInput)
{
MyOtherResult myOtherResult = null;
double myFunction(double input)
{
var result = ComputeMyResult(input);
myOtherResult = ComputeMyOtherResult(result, someOtherStuff);
return result;
}
var approximationResult = Calculate(startInput, myFunction);
var myOtherApproximationResult = myOtherResult;
// Do other stuff...
}
However, I'm not sure if this the best way of getting the 'other result', and if there is a side-effects-free way of doing this. The solution I have come up with only works because I know that the library I use applies this function iteratively, and that's not ideal. How would you go about solving this in C#? I've been racking my brain for two days and it's not clicking.
A delegate can (and usually does) have a target object. So: you can intentionally craft your target object as the state wrapper that you need for your logic. For example:
class MyState {
public double MyFunc(double x) {
// do whatever here, reading and writing to instance fields
// on MyState
}
}
...
var state = new MyState(/* additional values if needed */);
var result = ctx.Calculate(42, state.MyFunc);
I can't append an object to an array after a foreach loop. The object is okay, it contains all the right values which I found out through debugging.
In the end I want to have a custom Exercise object which contains a user-chosen number of custom ExerciseAnswer objects also. The array of ExerciseAnswer objects is the problem.
Here is the interesting part of my method:
static void CreateNewExerciseTest()
{
string? exerciseName = "Test Exercise";
string? exerciseTopic = "Test";
string exerciseQuestion = "Does it work?";
int numberOfAnswers = 2;
int numberOfApplicableAnswers = 0;
ExerciseAnswer[] exerciseAnswers = new ExerciseAnswer[2];
foreach (ExerciseAnswer answer in exerciseAnswers)
{
int exerciseAnswerId = GenerateId();
Console.WriteLine("\nEnter a name for this Exercise answer: ");
string? exerciseAnswerName = Console.ReadLine();
Console.WriteLine("Enter this answer for the Exercise: ");
string exerciseAnswerContent = Console.ReadLine();
Console.WriteLine("Enter y (yes) if this Exercise answer is applicable,
otherwise press n (no) or any other key: ");
char applicableAnswer = Console.ReadKey().KeyChar;
bool applicable = ExerciseAnswer.EvaluateExerciseAnswer(applicableAnswer);
if (applicable == true)
{
numberOfApplicableAnswers++;
}
ExerciseAnswer exerciseAnswer = new ExerciseAnswer(exerciseAnswerId,
exerciseAnswerName, exerciseAnswerContent, applicable);
exerciseAnswers.Append(exerciseAnswer);
// ...
}
}
This the GenerateId method:
static int GenerateId()
{
return ++id;
}
The array exerciseAnswers does not contain the ExerciseAnswer elements it should while the exerciseAnswer object in the line above does. Maybe the problem is related to the declaration and initialization of exerciseAnswers and the foreach loop.
Has somebody have an idea?
Thank you!
I believe you are using Append method from System.Linq namespace
public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element);
This method returns a new IEnumerable which contains your exerciceAnswer
With this piece of code you can understand what is going on:
var result = exerciseAnswers.Append(exerciseAnswer);
Console.WriteLine($"exerciseAnswers count = {exerciseAnswers.Count()}");
Console.WriteLine($"result count = {result.Count()}");
Console output:
exerciseAnswers count = 2
result count = 3
Append will just append to existing elements of array, since in your case size is already defined as 2(new ExerciseAnswer[2]) so it is not appending anything. What you can do is either have a new array and get the elements added to it or just get the index of element you are running the loop and replace same in the array.
Something like below:-
int elementIndex = Array.IndexOf(exerciseAnswers,answer);
exerciseAnswers[elementIndex] = exerciseAnswer;
I'm wondering if anyone can come up with a way to implement an array of numbers in a more memory efficient manner that will auto-organise itself into ranges. Example;
List testList = new List{1,2,3,4,5,6,7...};
vs
List<Range> testList = new List<Range>{1-3000,3002,4000-5000...};
Previously, I have asked a question just to confirm about whether or not this would in fact be a more memory efficient alternative. This question however pertains to actual application, how to implement this range list solution.
Index Array Storage Memory
I imagine this would perhaps need to be a custom list solution that would be a mix of ints and ranges. I'm picturing being able to .Add([int]) to the list, at which point it would determine if the value would cause a range to be added or to simply add the int value to the list.
Example
RangeList rangeList = new RangeList{1, 4, 7-9};
rangeList.Add(2);
//rangeList -> 1-2, 4, 7-9
rangeList.Add(3);
//rangeList -> 1-3, 4, 7-9
Details specific to my implementation
In my particular case, I'm analysing a very large document, line by line. Lines that meet a certain criteria need to be identified and then the overall list of line indexes need to be presented to the user.
Obviously displaying "Lines 33-32019 identified" is preferable to "Lines 33,34,35...etc". For this case, numbers will always be positive.
The first thing I would do is make a class which represents your range. You can provide some convenience like formatting as a string, and having an implicit cast from an int (This helps later implementation of the range list)
public class Range
{
public int Start{get; private set;}
public int End{get; private set;}
public Range(int startEnd) : this(startEnd,startEnd)
{
}
public Range(int start, int end)
{
this.Start = start;
this.End = end;
}
public static implicit operator Range(int i)
{
return new Range(i);
}
public override string ToString()
{
if(Start == End)
return Start.ToString();
return String.Format("{0}-{1}",Start,End);
}
}
You can then begin a simple implementation of the RangeList. By providing an Add method you can use a list initializer similar to List<T>:
public class RangeList : IEnumerable<Range>
{
private List<Range> ranges = new List<Range>();
public void Add(Range range)
{
this.ranges.Add(range);
}
public IEnumerator<Range> GetEnumerator()
{
return this.ranges.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator(){
return this.GetEnumerator();
}
}
At this point you can write some test code:
var rangeList = new RangeList(){
new Range(1,10),
15
};
foreach(var range in rangeList)
Console.WriteLine(range);
// Outputs:
// 1-10
// 15
Live example at this point: http://rextester.com/NCZSA71850
The next thing to do is provide an overload of Add which takes an int and finds the right range or adds a new one. A naive implemntation might look like the below (Assuming the addition of an Update method on range)
public void Add(int i)
{
// is it within or contiguous to an existing range
foreach(var range in ranges)
{
if(i>=range.Start && i<=range.End)
return; // already in a range
if(i == range.Start-1)
{
range.Update(i,range.End);
return;
}
if(i == range.End + 1)
{
range.Update(range.Start,i);
return;
}
}
// not in any ranges
ranges.Add(i);
}
Live example at this point: http://rextester.com/CHX64125
However this suffers from a few deficiencies
Does not merge ranges (say you already have 1-10 and 12-20 and you Add(11))
Does not re-order so if you have 1-5 and 20-25 and Add(7) this will be at the end not in the middle.
You can solve both problems by applying a sort after each addition, and some logic to determine if you should merge ranges
private void SortAndMerge()
{
ranges.Sort((a,b) => a.Start - b.Start);
var i = ranges.Count-1;
do
{
var start = ranges[i].Start;
var end = ranges[i-1].End;
if(end == start-1)
{
// merge and remove
ranges[i-1].Update(ranges[i-1].Start,ranges[i].End);
ranges.RemoveAt(i);
}
} while(i-- >1);
}
This needs to be called after every change to the list.
public void Add(Range range)
{
this.ranges.Add(range);
SortAndMerge();
}
public void Add(int value)
{
// is it within or contiguous to an existing range
foreach(var range in ranges)
{
if(value>=range.Start && value<=range.End)
return; // already in a range
if(value == range.Start-1)
{
range.Update(value,range.End);
SortAndMerge();
return;
}
if(value == range.End + 1)
{
range.Update(range.Start,value);
SortAndMerge();
return;
}
}
// not in any ranges
ranges.Add(value);
SortAndMerge();
}
Live example here: http://rextester.com/SYLARF47057
There are still some possible edge cases with this, which I urge you to work through.
UPDATE
The below will get this working as expected. This will merge up any added ranges/ints as you would expect and returns them correctly sorted. I've only changed the Add(Range) method, I think this is a fairly clean way of doing this.
public void Add(Range rangeToAdd)
{
var mergableRange = new List<Range>();
foreach (var range in ranges)
{
if (rangeToAdd.Start == range.Start && rangeToAdd.End == range.End)
return; // already exists
if (mergableRange.Any())
{
if (rangeToAdd.End >= range.Start - 1)
{
mergableRange.Add(range);
continue;
}
}
else
{
if (rangeToAdd.Start >= range.Start - 1
&& rangeToAdd.Start <= range.End + 1)
{
mergableRange.Add(range);
continue;
}
if (range.Start >= rangeToAdd.Start
&& range.End <= rangeToAdd.End)
{
mergableRange.Add(range);
continue;
}
}
}
if (!mergableRange.Any()) //Standalone range
{
ranges.Add(rangeToAdd);
}
else //merge overlapping ranges
{
mergableRange.Add(rangeToAdd);
var min = mergableRange.Min(x => x.Start);
var max = mergableRange.Max(x => x.End);
foreach (var range in mergableRange) ranges.Remove(range);
ranges.Add(new Range(min, max));
}
SortAndMerge();
}
Finally, we need if (ranges.Count > 1) in the SortAndMerge() method to prevent an index error when the first range is added.
And with that, I think this fully satisfies my question.
I'm dealing with some legacy data, where they store each record in one huge/large string (one string = one record)
In each string, they split the data in some sort of delimiters, but each of them actually defines a meaning, for example: \vToyota\cBlue\cRed\cWhite\s200mph\oAndrew\oJohn
\v means vehicle, \c is color, \s is speed \o is Owner... something like that
My task requires me to reformat the data so that if there are multiple fields of one characteristic, I have to rewrite it as: (for example) \vToyota\cBlue\c2Red\c3White\s200mph\oAndrew\o2John
Edited: Alright. #DarrenYoung's suggestions works! Now I have an array of vToyota cBlue cRed cWhite s200mph oAndrew oJohn. I tested on other data using the same method and it is working too. Now I just need help to find a way to rewrite the first letter of each string whenever they are repeated.
Thank you!
I found this an interesting little puzzle to see what I could do with LINQ. The following seems to work:
private string FixIt(string foo)
{
var newFoo = "\\" + string.Join("\\",
foo.Split(new[] {'\\'}, StringSplitOptions.RemoveEmptyEntries)
.GroupBy(s => s[0],
(c, g) =>
{
var cnt = 0;
return g.Select(x => cnt++ == 0
? x
: x[0] + cnt.ToString() + x.Substring(1));
})
.SelectMany(g => g));
return newFoo;
}
Input: \vToyota\cBlue\cRed\cWhite\s200mph\oAndrew\oJohn
Output: \vToyota\cBlue\c2Red\c3White\s200mph\oAndrew\o2John
That SelectMany is a handy thing to remember.
Because I thought this question was interesting I wrote up a program to do what I believe to be a reasonable solution. I started with a few principle assumptions:
In "old data" situations you probably don't know every single option that is going to show up in the records. Consequently whatever approach is taken needs to quickly and easily accommodate new types of delimiters and tags. For that reason I did not use a string.split approach (even though this is easier to read). Instead all tokens are declared at the beginning of the file. Anything can be a token whether or not it has a "\" in front of it.
The solution needs to gracefully handle records that don't conform to the standards
The option of parsing integers for multiple records needs to be able to be disabled per record type. Speed, for example, doesn't (seem) to be able to appear multiple times per record. So, setting the value for speed to false in the "ALLOW_MULTIPLE" variable turns this parsing off, ensuring the correct output value.
In my solution I also created separate classes for readability and so the code could be quickly investigated. Although I would not suggest that this is production ready, the following should go a long ways towards solving the issue. Best of luck!
// Just paste the rest of this into a new console application to see it work!
public class Program
{
private static readonly List<string> TOKENS = new List<string> {#"\v", #"\c", #"\o", #"\s"};
private static readonly List<string> DISPLAY = new List<string> {"Vehicle", "Color", "Owner", "Speed"};
private static readonly List<bool> ALLOW_MULTIPLE = new List<bool> {false, true, true, false};
private class RecordEntry
{
public string Value { get; set; }
public int Index { get; set; }
public string DataType { get; set; }
public override string ToString() { return DataType + ": " + Value; }
}
private class ParsedRecord
{
private List<RecordEntry> entries = new List<RecordEntry>();
public List<RecordEntry> Entries { get { return entries; } }
}
public static void Main(string[] args)
{
// sample records (second has a \m which is ignored since it isn't a recognized token)
var records = new[] {#"\vToyota\cBlue\c2Red\c3White\s200mph\oAndrew\o2John",
#"\vChevy\c2Orange\cGreen\s50mph\o2Bob\mWhite"};
var parsedData = new List<ParsedRecord>();
foreach (var record in records)
{
// character by character parsing
var currentParseRecord = new ParsedRecord();
parsedData.Add(currentParseRecord);
var currentRecord = new StringBuilder(record);
var currentToken = new StringBuilder();
for (var parseIdx = 0; parseIdx < currentRecord.Length; parseIdx++)
{
currentToken.Append(currentRecord[parseIdx]);
var recordIdx = 0;
var index = TOKENS.IndexOf(currentToken.ToString());
if (index < 0) continue;
// current char is used up now (was part of the token)
parseIdx++;
if (ALLOW_MULTIPLE[index] && currentRecord.Length > parseIdx + 1)
{
// assuming less than 10 records max - if more, would need to pull multiple numeric values here
if (!Int32.TryParse(currentRecord[parseIdx] + "", out recordIdx)) recordIdx = 0;
else parseIdx++;
}
// find the next token or end of string
int valueLength = FindNextToken(currentRecord, parseIdx) - parseIdx;
if (valueLength <= 0) valueLength = currentRecord.Length - parseIdx;
currentParseRecord.Entries.Add(new RecordEntry
{
DataType = DISPLAY[index],
Index = recordIdx,
Value = currentRecord.ToString(parseIdx, valueLength)
});
parseIdx += valueLength - 1;
currentToken.Clear();
}
}
}
private static int FindNextToken(StringBuilder value, int currentIndex)
{
for (var searchIdx = currentIndex; searchIdx < value.Length; searchIdx++) {
if (TOKENS.Any(checkToken => value.Length > searchIdx + checkToken.Length &&
value.ToString(searchIdx, checkToken.Length) == checkToken)) {
return searchIdx;
}
}
return -1;
}
}
I wish to create the following test in NUnit for the following scenario: we wish to test the a new calculation method being created yields results similar to that of an old system. An acceptable difference (or rather a redefinition of equality) between all values has been defined as
abs(old_val - new_val) < 0.0001
I know that I can loop through every value from the new list and compare to values from the old list and test the above condition.
How would achieve this using Nunit's CollectionAssert.AreEqual method (or some CollectionAssert method)?
The current answers are outdated. Since NUnit 2.5, there is an overload of CollectionAssert.AreEqual that takes a System.Collections.IComparer.
Here is a minimal implementation:
public class Comparer : System.Collections.IComparer
{
private readonly double _epsilon;
public Comparer(double epsilon)
{
_epsilon = epsilon;
}
public int Compare(object x, object y)
{
var a = (double)x;
var b = (double)y;
double delta = System.Math.Abs(a - b);
if (delta < _epsilon)
{
return 0;
}
return a.CompareTo(b);
}
}
[NUnit.Framework.Test]
public void MyTest()
{
var a = ...
var b = ...
NUnit.Framework.CollectionAssert.AreEqual(a, b, new Comparer(0.0001));
}
Well there is method from the NUnit Framework that allows me to do tolerance checks on collections. Refer to the Equal Constraint. One uses the AsCollection and Within extension methods. On that note though I am not 100% sure regarding the implications of this statement made
If you want to treat the arrays being compared as simple collections,
use the AsCollection modifier, which causes the comparison to be made
element by element, without regard for the rank or dimensions of the
array.
[Test]
//[ExpectedException()]
public void CheckLists_FailsAt0()
{
var expected = new[] { 0.0001, 0.4353245, 1.3455234, 345345.098098 };
var result1 = new[] { -0.0004, 0.43520, 1.3454, 345345.0980 };
Assert.That(result1, Is.EqualTo(expected).AsCollection.Within(0.0001), "fail at [0]"); // fail on [0]
}
[Test]
//[ExpectedException()]
public void CheckLists_FailAt1()
{
var expected = new[] { 0.0001, 0.4353245, 1.3455234, 345345.098098 };
var result1a = new[] { 0.0001000000 , 0.4348245000 , 1.3450234000 , 345345.0975980000 };
Assert.That(result1a, Is.EqualTo(expected).AsCollection.Within(0.0001), "fail at [1]"); // fail on [3]
}
[Test]
public void CheckLists_AllPass_ForNegativeDiff_of_1over10001()
{
var expected = new[] { 0.0001, 0.4353245, 1.3455234, 345345.098098 };
var result2 = new[] { 0.00009900 , 0.43532350 , 1.34552240 , 345345.09809700 };
Assert.That(result2, Is.EqualTo(expected).AsCollection.Within(0.0001)); // pass
}
[Test]
public void CheckLists_StillPass_ForPositiveDiff_of_1over10001()
{
var expected = new[] { 0.0001, 0.4353245, 1.3455234, 345345.098098 };
var result3 = new[] { 0.00010100 , 0.43532550 , 1.34552440 , 345345.09809900 };
Assert.That(result3, Is.EqualTo(expected).AsCollection.Within(0.0001)); // pass
}
NUnit does not define any delegate object or interface to perform custom checks to lists, and determine that a expected result is valid.
But I think that the best and simplest option is writing a small static method that achieve your checks:
private const float MIN_ACCEPT_VALUE = 0.0001f;
public static void IsAcceptableDifference(IList collection, IList oldCollection)
{
if (collection == null)
throw new Exception("Source collection is null");
if (oldCollection == null)
throw new Exception("Old collection is null");
if (collection.Count != oldCollection.Count)
throw new Exception("Different lenghts");
for (int i = 0; i < collection.Count; i++)
{
float newValue = (float)collection[i];
float oldValue = (float)oldCollection[i];
float difference = Math.Abs(oldValue - newValue);
if (difference < MIN_ACCEPT_VALUE)
{
throw new Exception(
string.Format(
"Found a difference of {0} at index {1}",
difference,
i));
}
}
}
You've asked how to achieve your desired test using a CollectionAssert method without looping through the list. I'm sure this is obvious, but looping is exactly what such a method would do...
The short answer to your exact question is that you can't use CollectionAssert methods to do what you want. However, if what you really want is an easy way to compare lists of floating point numbers and assert their equality, then read on.
The method Assert.AreEqual( double expected, double actual, double tolerance ) releases you from the need to write the individual item assertions yourself. Using LINQ, you could do something like this:
double delta = 0.0001;
IEnumerable<double> expectedValues;
IEnumerable<double> actualValues;
// code code code
foreach (var pair in expectedValues.Zip(actualValues, Tuple.Create))
{
Assert.AreEqual(pair.Item1, pair.Item2, delta, "Collections differ.");
}
If you wanted to get fancier, you could pull this out into a method of its own, catch the AssertionException, massage it and rethrow it for a cleaner interface.
If you don't care about which items differ:
var areEqual = expectedValues
.Zip(actualValues, Tuple.Create)
.Select(tup => Math.Abs(tup.Item1 - tup.Item2) < delta)
.All(b => b);
Assert.IsTrue(areEqual, "Collections differ.");