List or dictionary in C# - c#

I need two lists as output, one of which is a list of index and the other is of the corresponding value till the condition is satisfied..
//initializing the first value of TotalDebts
double TotalDebts = 30000;
for (int i = 0; i < 250; i++)
{
if (TotalDebts > 0)
{
double DebtsLessIncome = Convert.ToDouble(TotalDebts - 1000);
double InterestCharged = Convert.ToDouble((DebtsLessIncome * 5) / 100);
double InterestDebt = Convert.ToDouble(DebtsLessIncome + InterestCharged);
double InterestDebtMLE = Convert.ToDouble(InterestDebt + 500);
double TotalDebts = Convert.ToDouble(InterestDebtMLE);
//how to add TotalDebts in list or dictionary from each loop as index 0,1,2 and so on
List<double> AllDebits = new List<double>();
AllDebits.Add(TotalDebts);
// how to retrieve each index and value and store index in one list and value in second list
}
}

Based on "how to retrieve each index and value" - I assume you want to access the data by index (=year?) - dictionary works fine for that.
double TotalDebts = 30000;
Dictionary<int, double> dResult = new Dictionary<int, double>();
for (int i = 0; i < 250; i++)
{
if (TotalDebts > 0)
{
double DebtsLessIncome = Convert.ToDouble(TotalDebts - 1000);
double InterestCharged = Convert.ToDouble((DebtsLessIncome * 5) / 100);
double InterestDebt = Convert.ToDouble(DebtsLessIncome + InterestCharged);
double InterestDebtMLE = Convert.ToDouble(InterestDebt + 500);
TotalDebts = Convert.ToDouble(InterestDebtMLE);
dResult.Add(i, TotalDebts);
}
}
Notes
Dictionary does not support ordered iteration so if you need to output list of year/debt pairs use some other data structure (i.e. List<KeyValuePair<int, decimal>>).
usually one would use decimal for money values instead of float/double.
Update (split dictionary into 2 lists)
List<int> lIndex = dResult.Select(x => x.Key).ToList();
List<double> lDepts = dResult.Select(x => x.Value).ToList();

I would use a list of tuples like this:
double TotalDebts = 30000;
list<Tuple<int, double>> AllDebits = new list<Tuple<int, double>>();
for (int i = 0; i < 250; i++)
{
if (TotalDebts > 0)
{
double DebtsLessIncome = Convert.ToDouble(TotalDebts - 1000);
double InterestCharged = Convert.ToDouble((DebtsLessIncome * 5) / 100);
double InterestDebt = Convert.ToDouble(DebtsLessIncome + InterestCharged);
double InterestDebtMLE = Convert.ToDouble(InterestDebt + 500);
TotalDebts = Convert.ToDouble(InterestDebtMLE);
AllDebits.Add(new Tuple<int,double>(i, TotalDebts));
}
}
It really depends on what you'll be using this for. If the data is really just to display in a list then this is fine. However, if the index is to be used for something else (id perhaps?) then a Dictionary (as per fubo's answer) would be much better, as the index has meaning and needs to be unique (Dictionaries don't allow duplicate keys).

Related

How would you divide all the negative values in a dictionary in C#?

I have a Dictionary<string, double>. I looped through the values to remove all the positive double values.
I need to divide the remaining negative values together and then round it to 10 decimal points.
double divisionSum = 1;
foreach (var entry in dic.Values)
{
divisionSum /= entry;
}
This doesn't work as 1 divided by a negative number does not return it's initial value for the first instance of division.
For example, if the values of the Dictionary was -2, -4, -8, -5 I would want divisionSum to equal 0.0125. Also cannot use any Math() methods
Loop through your list (ok, it's a dictionary, but you only care about the values, so it's basically a list) starting at index 1 not the default of 0, but first assign the value of index 0 to your result.
var foo = new List<double>{-1, -10, -3, -5};
double result = foo[0];
for (int i = 1; i < foo.Count; i++)
{
result /= foo[i];
}
result = Math.Round(result, 10);
result.Dump();
and since the question was modified after the above answer to require Math library be excluded, here's a variant:
var foo = new List<double>{-1, -10, -3, -5};
double result = foo[0];
for (int i = 1; i < foo.Count; i++)
{
result /= foo[i];
}
result = Convert.ToDouble(result.ToString("#.0000000000"));
result.Dump();
It works if you start with the first value instead of 1. To have better control on accessing individual value, I have converted the values from the dictionary into an array:
double[] values = dic.Values.ToArray();
double divisionSum = values[0];
for (int i = 1; i < values.Length; i++) {
divisionSum /= values[i];
}
// divisionSum => 0.0125
Note that the for-loop starts at index 1 to skip the first value at index 0 which was already assigned to divisionSum.
The advantage of a dictionary is that you can look up values very quickly by key. They are not particularly good when you have to loop over the values. It would be better to use a list or array for this purpose.
The following corrects the code so that divisionSum is initialised with the first value and the loop divides in the remaining values. The last line does the rounding to 10 decimal places without using any Math() methods, which was a requirement.
var valuesList = dic.Values.ToList();
var divisionSum = valuesList[0];
for (var index = 1; index < valuesList.Count; index++)
{
divisionSum /= valuesList[index];
}
divisionSum = Convert.ToDouble(divisionSum.ToString("#.0000000000", CultureInfo.InvariantCulture));
You can query the dictionary with a help of Linq:
using System.Linq;
...
// Let me keep the name; result looks a better however
double divisionSum = dic
.Values
.Where(item => item < 0)
.DefaultIfEmpty(0.0) // put the default value here: 0, 1 or whatever
.Aggregate((s, a) => s / a);
For no Linq solution we have to initialize within the loop (let's have firstTime flag) with the 1st negative item:
double divisionSum = 0.0; // put default value here: 0, 1 or whatever
bool firstTime = true;
foreach (var item in dic.Values) {
if (item < 0) {
divisionSum = firstTime ? item : divisionSum / item;
firstTime = false;
}
}

How to fill a SortedList with a Dictionary as TValue

I want to use the following method but as hobbyist programmer I cannot understand how to fill(format?) the SortedList that will be used as input to the method.
I have a sql table with DateTime and a Value that will have always "close" string associated (see code)
looked at several answers but no conclusions at all
public static void AddBollingerBands(ref SortedList<DateTime, Dictionary<string, double>> data, int period, int factor)
{
double total_average = 0;
double total_squares = 0;
for (int i = 0; i < data.Count(); i++)
{
total_average += data.Values[i]["close"];
total_squares += Math.Pow(data.Values[i]["close"], 2);
if (i >= period - 1)
{
double total_bollinger = 0;
double average = total_average / period;
double stdev = Math.Sqrt((total_squares - Math.Pow(total_average,2)/period) / period);
data.Values[i]["bollinger_average"] = average;
data.Values[i]["bollinger_top"] = average + factor * stdev;
data.Values[i]["bollinger_bottom"] = average - factor * stdev;
.......
......
Using .Values is a get operation only. The result is not a reference to the element in your sorted list, but instead an immutable var.
Ignoring the issues with not using SortedList properly, you can only change the value of a sortedList if you reference the element directly via its key:
data[keyValue]["total_bollinger"] = average;
The above line of code would update that value in the list accordingly.
Rather than iterate over the list via data.Count(), I would recommend iterating over the keys like this:
var keys = data.Keys;
foreach(var key in data.Keys)
{
double total_bollinger = 0;
double average = total_average / period;
double stdev = Math.Sqrt((total_squares - Math.Pow(total_average, 2) / period) / period);
data[key]["total_bollinger"] = total_average;
}

pick between several actions using percentage

I have an Object with X amount of hobbies, each hobby has a value between 0-100 (representing %), and together they add up to 100.
Every morning I want to run a function that decides what that Object is going to do, and the higher value a hobby has, the more likely they are to be chosen.
I cannot figure out how to translate that last part into code. Using these variables for example:
int fishing = 25;
int sleeping = 25;
int drinking = 50;
int running = 0;
This will work for you:
class Program
{
private static List<KeyValuePair<string, int>> Actions = new List<KeyValuePair<string, int>>()
{
new KeyValuePair<string, int>("fishing", 25),
new KeyValuePair<string, int>("sleeping", 25),
new KeyValuePair<string, int>("drinking", 5),
new KeyValuePair<string, int>("running", 0),
};
static void Main(string[] args)
{
var result = Actions.OrderByDescending(r => r.Value).First();
Console.WriteLine(result.Key);
}
}
This assumes that you already have the actions and their percentage. Now if any 2 (or more) of the items have the same percentage, it will show the first one added to the list.
EDIT
Sorry didn't get the requirement for randomization. Try this:
var listOfActionsToTake = new List<string>() { "fishing", "sleeping", "drinking", "running" };
var listOFActions = new List<KeyValuePair<string, int>>();
var rand = new Random();
var currentPercentage = 101;
for (int i = 0; i < listOfActionsToTake.Count; i++)
{
var current = rand.Next(currentPercentage);
if (i == listOfActionsToTake.Count - 1)
{
current = currentPercentage - 1;
}
listOFActions.Add(new KeyValuePair<string, int>(listOfActionsToTake[i], current));
currentPercentage -= current;
}
foreach (var action in listOFActions)
{
Console.WriteLine($"{action.Key}: {action.Value}");
}
var result = listOFActions.OrderByDescending(r => r.Value).First();
Console.WriteLine(result.Key);
Like this, no matter how many values you add to the listOfActionsToTake you will always end up with a sum of 100 between them, and the biggest will be selected
I found this example and it seems to make sense. You add the values to get a cumulative value. If the random value created is less then the cumulative probability, then the current item is the selected.
Basically, if that condition is false, then you ignore the current items probability from the condition. So the next item to be compared now has a higher probability to be chosen.
For example:
// 1. RandNum = 0.4
// 2. cumulative = 0.0
// 3. fishing = 0.25
// 4. cumulative = cumulative + fishing = 0.25
// 5. RandNum < cumulative ? false
The random number was 0.4 and fishing has a probably of 0.25 to be selected. Since fishing's probability is lower then the random number, we add it to the cumulative value and essentially ignore it in the next item. The next item will now have a higher probability to be chosen because now it's just sleeping and drinking. They now both have a .50 probability to be chosen.
// 1. RandNum = 0.4
// 2. cumulative = 0.25
// 3. sleeping = 0.25
// 4. cumulative = cumulative + sleeping = .50
// 5. RanNum < cumulative ? true
Although you would have to modify it to account for when the percentages are equal since it'll just take the first one. You could check if the percentages are the same after finding an inital action to do. If the randomly chosen action has the same percentage as another, then randomly pick one of them.
static void Main(string[] args)
{
int fishing = 25;
int sleeping = 25;
int drinking = 50;
int running = 0;
List<KeyValuePair<string, double>> elements = new List<KeyValuePair<string, double>>();
elements.Add(new KeyValuePair<string, double>("fishing", (fishing * (1.0 / 100.0)))); // 25 * (1 /100) = .25
elements.Add(new KeyValuePair<string, double>("sleeping", (sleeping * (1.0 / 100.0))));
elements.Add(new KeyValuePair<string, double>("drinking", (drinking * (1.0 / 100.0))));
elements.Add(new KeyValuePair<string, double>("running", (running * (1.0 / 100.0))));
Random r = new Random();
double rand = r.NextDouble();
double cumulative = 0.0;
string selectedElement = "";
for (int i = 0; i < elements.Count; i++)
{
cumulative += elements[i].Value;
if (rand < cumulative)
{
selectedElement = elements[i].Key;
break;
}
}
Console.WriteLine("Selected Element: " + selectedElement);
Console.ReadKey();
}
// Test 1: rand: 0.522105917
// cumulative: 1
// Selected Element: drinking
// Test 2: rand: 0.49201479
// cumulative: 0.5
// Selected Element: sleeping
This should do it and will work when they don't add to 100 too.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static Dictionary<string, int> activities = new Dictionary<string, int>
{
{ "running", 0 },
{ "fishing", 25 },
{ "sleeping", 25 },
{ "drinking", 50 }
};
static void Main(string[] args)
{
int sum = activities.Sum(a => a.Value);
int rand = new Random().Next(sum);
int total = -1;
string activity = activities.SkipWhile(a => (total += a.Value) < rand).First().Key;
Console.WriteLine(activity);
}
}

Substracting adjacent two values in a list C#

There is a list called cardReaderHistory . That contains some time records in ordered fashion as follows,
InTime1
OutTime1
InTime2
OutTime2
InTime3
OutTime3
InTime4
OutTime4.....furthermore..
What I need is Calculate Working time by (OutTime1 - Intime1) + (OutTime1 - Intime1).....
double
How could I do this in C#...????
double hr = ((outTime1 - inTime1)+(OutTime2 - Intime2)+...);
Thank you..
Poor Beginner
You can filter the input sequence based on the index, and then zip the two sequences:
var inTimes = source.Where((x, index) => index % 2 == 0);
var outTimes = source.Where((x, index) => index % 2 == 1);
var result = inTimes.Zip(outTimes, (inTime, outTime) => outTime - inTime).Sum();
If you don't need the intermediary values, you can also do this:
var result = source.Select((x, index) => (index % 2 == 0) ? -x : x).Sum();
Assuming your cardReaderHistory list is a list of doubles:
List<double> cardReaderHistory = new List<double>(); //fill it somewhere
double result;
for(int i = 0; i < cardReaderHistory.Count(); i++)
{
if(i%2==0) //If it is equal
result -= cardReaderHistory[i];
else //its unequal
result += cardReaderHistory[i];
}
You just loop over your values and add or subtract based on if its even or not.
Something like this...
List<double> l = new List<double> { 1, 2, 3, 4, 5, 6, 7, 8 };
double hr = 0;
for (int i = 0; i < l.Count; i++)
{
hr += i%2 == 0 ? -l[i] : l[i];
}
It seems plausible that the list contains datetimes and not hours. Currently none of the other answers handles that. Here's my take.
var cardReaderHistory = new List<DateTime> {DateTime.Now.AddMinutes(-120), DateTime.Now.AddMinutes(-100), DateTime.Now.AddMinutes(-20), DateTime.Now};
var hours = cardReaderHistory.Split(2).Select(h => (h.Last() - h.First()).TotalHours).Sum();
Split is an extension method
static class ExtentionsMethods
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> seq, int size)
{
while (seq.Any())
{
yield return seq.Take(size);
seq = seq.Skip(size);
}
}
}

Looking for a Histogram Binning algorithm for decimal data

I need to generate bins for the purposes of calculating a histogram. Language is C#. Basically I need to take in an array of decimal numbers and generate a histogram plot out of those.
Haven't been able to find a decent library to do this outright so now I'm just looking for either a library or an algorithm to help me do the binning of the data.
So...
Are there any C# libraries out there that will take in an array of decimal data and output a binned histogram?
Is there generic algorithm for building the bins to be used in generated a histogram?
Here is a simple bucket function I use. Sadly, .NET generics doesn't support a numerical type contraint so you will have to implement a different version of the following function for decimal, int, double, etc.
public static List<int> Bucketize(this IEnumerable<decimal> source, int totalBuckets)
{
var min = source.Min();
var max = source.Max();
var buckets = new List<int>();
var bucketSize = (max - min) / totalBuckets;
foreach (var value in source)
{
int bucketIndex = 0;
if (bucketSize > 0.0)
{
bucketIndex = (int)((value - min) / bucketSize);
if (bucketIndex == totalBuckets)
{
bucketIndex--;
}
}
buckets[bucketIndex]++;
}
return buckets;
}
I got odd results using #JakePearson accepted answer. It has to do with an edge case.
Here is the code I used to test his method. I changed the extension method ever so slightly, returning an int[] and accepting double instead of decimal.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Random rand = new Random(1325165);
int maxValue = 100;
int numberOfBuckets = 100;
List<double> values = new List<double>();
for (int i = 0; i < 10000000; i++)
{
double value = rand.NextDouble() * (maxValue+1);
values.Add(value);
}
int[] bins = values.Bucketize(numberOfBuckets);
PointPairList points = new PointPairList();
for (int i = 0; i < numberOfBuckets; i++)
{
points.Add(i, bins[i]);
}
zedGraphControl1.GraphPane.AddBar("Random Points", points,Color.Black);
zedGraphControl1.GraphPane.YAxis.Title.Text = "Count";
zedGraphControl1.GraphPane.XAxis.Title.Text = "Value";
zedGraphControl1.AxisChange();
zedGraphControl1.Refresh();
}
}
public static class Extension
{
public static int[] Bucketize(this IEnumerable<double> source, int totalBuckets)
{
var min = source.Min();
var max = source.Max();
var buckets = new int[totalBuckets];
var bucketSize = (max - min) / totalBuckets;
foreach (var value in source)
{
int bucketIndex = 0;
if (bucketSize > 0.0)
{
bucketIndex = (int)((value - min) / bucketSize);
if (bucketIndex == totalBuckets)
{
bucketIndex--;
}
}
buckets[bucketIndex]++;
}
return buckets;
}
}
Everything works well when using 10,000,000 random double values between 0 and 100 (exclusive). Each bucket has roughly the same number of values, which makes sense given that Random returns a normal distribution.
But when I changed the value generation line from
double value = rand.NextDouble() * (maxValue+1);
to
double value = rand.Next(0, maxValue + 1);
and you get the following result, which double counts the last bucket.
It appears that when a value is same as one of the boundaries of a bucket, the code as it is written puts the value in the incorrect bucket. This artifact doesn't appear to happen with random double values as the chance of a random number being equal to a boundary of a bucket is rare and wouldn't be obvious.
The way I corrected this is to define what side of the bucket boundary is inclusive vs. exclusive.
Think of
0< x <=1 1< x <=2 ... 99< x <=100
vs.
0<= x <1 1<= x <2 ... 99<= x <100
You cannot have both boundaries inclusive, as the method wouldn't know which bucket to put it in if you have a value that is exactly equal to a boundary.
public enum BucketizeDirectionEnum
{
LowerBoundInclusive,
UpperBoundInclusive
}
public static int[] Bucketize(this IList<double> source, int totalBuckets, BucketizeDirectionEnum inclusivity = BucketizeDirectionEnum.UpperBoundInclusive)
{
var min = source.Min();
var max = source.Max();
var buckets = new int[totalBuckets];
var bucketSize = (max - min) / totalBuckets;
if (inclusivity == BucketizeDirectionEnum.LowerBoundInclusive)
{
foreach (var value in source)
{
int bucketIndex = (int)((value - min) / bucketSize);
if (bucketIndex == totalBuckets)
continue;
buckets[bucketIndex]++;
}
}
else
{
foreach (var value in source)
{
int bucketIndex = (int)Math.Ceiling((value - min) / bucketSize) - 1;
if (bucketIndex < 0)
continue;
buckets[bucketIndex]++;
}
}
return buckets;
}
The only issue now is if the input dataset has a lot of min and max values, the binning method will exclude many of those values and the resulting graph will misrepresent the dataset.

Categories