Constructing a list of strings following a statistical breakdown - c#

I'm building a function that will construct a list of strings by using a statistical breakdown provided as an argument. I'm looking for a more elegant and precise way to accomplish this. Here is a working example of what I have so far. This is working currently for simple cases, but I anticipate more complex cases causing issue (math rounding, etc.)
public static void StatisticalList()
{
List<string> statisticalList = new List<string>();
int totalCount = 500;
// end goal is 30% of our result list has the value "1"
List<string> entry1 = new List<string>() { "30", "1" };
// end goal is 40% of our result list has the value "2"
List<string> entry2 = new List<string>() { "40", "2" };
// end goal is 30% of our result list has the value "3"
List<string> entry3 = new List<string>() { "30", "3" };
List<List<string>> container = new List<List<string>>(){entry1, entry2, entry3};
foreach(List<string> entry in container)
{
double doub = Convert.ToDouble(entry[0]);
double percentage = doub / 100;
double numberOfElements = (double) percentage * totalCount;
for (int i = 0; i < numberOfElements; i++)
{
statisticalList.Add(entry[1]);
}
}
foreach (string i in statisticalList)
{
Console.WriteLine(i);
}
}

There are a couple of things I would do to make this "more elegant". First, I would create a class to represent a rule, which is a "Goal Percent" and a "Value":
public class StatisticalRule
{
public double PercentGoal { get; set; }
public double Value { get; set; }
}
Then, I would create a method that takes in a list of these rules, along with a desired list size, and returns a list populated with the values. I've added some logic to your code to adjust the percentage of each item in case the total is less than or greater than 100 percent (another option would be to just throw an exception if the totals don't add up to 100).
For example, if someone added 3 items where one was 60%, one was 50%, and another was 40%, we have to adjust those amounts (we can't fill 150%). So what I've done is determine the total amount we need to adjust (-50 in this example), and then, for each item, calculate what percent of the total that item's PercentGoal represents, apply it to the adjustment amount, and apply that to the rule's percentGoal (so in this example, 60% = 40%, 50% = 33%, and 40% = 27%), and then populate the list with these adjusted percentages.
I also use Enumerable.Repeat to add items to the list, which is a little more elegant than the loop construct.
public static List<double> GetStatisticalList(List<StatisticalRule> rules, int totalCount)
{
List<double> statisticalList = new List<double>();
// Capture any difference between our total percentages and 100 percent
var totalPct = rules.Sum(r => r.PercentGoal);
var pctDiff = 100 - totalPct;
foreach (var rule in rules)
{
// Calculate the percentage of the total this value represents
var pctOfTotal = rule.PercentGoal / totalPct * 100;
// Calculate the amount we need to adjust this
// percentage by so the totals equal 100
var pctAdjustment = pctDiff * pctOfTotal / 100;
// Determine the number of items to add by adding our adjustment to
// our percentage goal and applying that percentage to the totalCount
var numItems = (int) ((rule.PercentGoal + pctAdjustment) / 100 * totalCount);
// Add the adjusted amount of this value to our list
statisticalList.AddRange(Enumerable.Repeat(rule.Value, numItems));
}
return statisticalList;
}
Notice I'm returning a list of double instead of string. The method itself should work directly with the data type it expects when possible, and leave it to the caller to do any conversions. This makes for cleaner, more intentional code, and creates fewer assumptions.
To use this code with your example above, you would do something like this:
static void Main()
{
// Create our rules
var statRules = new List<StatisticalRule>
{
new StatisticalRule {PercentGoal = 30, Value = 1},
new StatisticalRule {PercentGoal = 40, Value = 2},
new StatisticalRule {PercentGoal = 30, Value = 3},
};
// Get our 500 item stat list with rules applied
var statList = GetStatisticalList(statRules, 500);
// Display the statistics
Console.WriteLine($"Our statistics list contains {statList.Count} items:");
foreach (var uniqueValue in statList.Distinct())
{
var valueCount = statList.Count(i => i == uniqueValue);
Console.WriteLine(" - Value: {0}, Count: {1}, Percent of Total: {2}%",
uniqueValue, valueCount, (double)valueCount / statList.Count * 100);
}
Console.Write("\nDone!\nPress any key to continue...");
Console.ReadKey();
}
Output

Related

How can I use C#/LINQ to calculate weighted averages

This is to process stock data; the data is in this format:
public class A
{
public int Price;
public int Available;
}
let's take this data for example:
var items = new List<A>
{
new A { Price = 10, Available = 1000 },
new A { Price = 15, Available = 500 },
new A { Price = 20, Available = 2000 },
};
my query returns the average price for a specific volume, so for example:
if I have a requested volume of 100, my average price is 10
if I have a requested volume of 1200, I take the first 1000 at a price of 10, then the next 200 at a price of 15
etc
I have implemented that in C#, but I am trying to find if this could be done with LINQ directly with the database iterator.
I get data that is already sorted by price, but I don't see how to solve this without iteration.
Edit:
this is the code:
public static double PriceAtVolume(IEnumerable<A> Data, long Volume)
{
var PriceSum = 0.0;
var VolumeSum = 0L;
foreach (var D in Data)
{
if (D.Volume < Volume)
{
PriceSum += D.Price * D.Volume;
VolumeSum += D.Volume;
Volume -= D.Volume;
}
else
{
PriceSum += D.Price * Volume;
VolumeSum += Volume;
Volume = 0;
}
if (Volume == 0) break;
}
return PriceSum / VolumeSum;
}
and the test code:
var a = new List<A>
{
new A { Price = 10, Volume = 1000 },
new A { Price = 15, Volume = 500 },
new A { Price = 20, Volume = 2000 }
};
var P0 = PriceAtVolume(a, 100);
var P1 = PriceAtVolume(a, 1200);
Clarification:
Above I said I'd like to move it to LINQ to use the database iterator, so I'd like to avoid scanning the entire data and stop iterating when the answer is calculated. The data is already sorted by price in the database.
This is probably the most Linqy you can get. It uses the Aggregate method, and specifically the most complex of the three overloaded versions of Aggregate, that accepts three arguments. The first argument is the seed, and it is initialized with a zeroed ValueTuple<long, decimal>. The second argument is the accumulator function, with the logic to combine the seed and the current element into a new seed. The third argument takes the final accumulated values and projects them to the desirable average.
public static decimal PriceAtVolume(IEnumerable<A> data, long requestedVolume)
{
return data.Aggregate(
(Volume: 0L, Price: 0M), // Seed
(sum, item) => // Accumulator function
{
if (sum.Volume == requestedVolume)
return sum; // Goal reached, quick return
if (item.Available < requestedVolume - sum.Volume)
return // Consume all of it
(
sum.Volume + item.Available,
sum.Price + item.Price * item.Available
);
return // Consume part of it (and we are done)
(
requestedVolume,
sum.Price + item.Price * (requestedVolume - sum.Volume)
);
},
sum => sum.Volume == 0M ? 0M : sum.Price / sum.Volume // Result selector
);
}
Update: I changed the return type from double to decimal, because a decimal is the preferred type for currency values.
Btw in case that this function is called very often with the same data, and the list of data is huge, it could be optimized by storing the accumulated summaries in a List<(long, decimal)>, and applying BinarySearch to quickly find the desirable entry. It becomes complex though, and I don't expect that the prerequisites for the optimization will come up very often.
This is working as well (although not a one-liner):
private static decimal CalculateWeighedAverage(List<A> amountsAndPrices, int requestedVolume)
{
int originalRequestedVolume = requestedVolume;
return (decimal)amountsAndPrices.Sum(amountAndPrice =>
{
int partialResult = Math.Min(amountAndPrice.Available, requestedVolume) * amountAndPrice.Price;
requestedVolume = Math.Max(requestedVolume - amountAndPrice.Available, 0);
return partialResult;
}) / originalRequestedVolume;
}
Take the sum of price * available as long as the requested volume is bigger than 0 and subtracting the amount of every item in the list in each "sum iteration". Finally divide by the original requested volume.
You could do something to generate the items' prices as a sequence. e.g.
public class A
{
public int Price;
public int Available;
public IEnumerable<int> Inv => Enumerable.Repeat(Price, Available);
}
var avg1 = items.SelectMany(i => i.Inv).Take(100).Average(); // 10
var avg2 = items.SelectMany(i => i.Inv).Take(1200).Average(); // 10.8333333333333
I think the best you can do with LINQ is minimize the running total computation done on the server and compute most of it on the client, but minimize the amount downloaded from the server.
I assume the items are already projected down to the two minimum columns (Price, Availability). If not, a Select can be added before pulling the data from the database into orderedItems.
// find price of last item needed; worst case there won't be one
var lastPriceItem = items.Select(i => new { i.Price, RT = items.Where(it => it.Price <= i.Price).Sum(it => it.Available) }).FirstOrDefault(irt => irt.RT > origReqVol);
// bring over items below that price
var orderedItems = items.OrderBy(i => i.Price).Where(i => i.Price <= lastPriceItem.Price).ToList();
// compute running total on client
var rtItems = orderedItems.Select(i => new {
Item = i,
RT = orderedItems.Where(i2 => i2.Price <= i.Price).Sum(i2 => i2.Available)
});
// computer average price
var reqVol = origReqVol;
var ans = rtItems.Select(irt => new { Price = irt.Item.Price, Quantity = Math.Min((reqVol -= irt.Item.Available)+irt.Item.Available, irt.Item.Available) })
.Sum(pq => pq.Price * pq.Quantity) / (double)origReqVol;

Searching for all combinations of numbers from a set of numbers which equal a target, code misses some but not all

I used this question Find out which combinations of numbers in a set add up to a given total which uses a C# example to attempt to find all the possible combinations of numbers which add up to a given number.
I used the code as provided and have attempted to understand it and I think I do, however, I can't figure out why this error is occurring.
Here is the setup:
public class Program
{
public static void Main(string[] args)
{
// subtotal list
List<double> totals = new List<double>(new double[] { 17.5, 14.3, 10.9, 7.8, 6.3, 3.8, 3.2, 2.7, 1.8, 1.0 });
List<double> totals2 = new List<double>(new double[] { 17.5, 14.3, 7.8, 6.3, 3.2, 1.8});
// get matches
List<double[]> results = Knapsack.MatchTotal(50.9, totals2);
// print results`
foreach (var result in results)
{
Console.WriteLine(string.Join(",", result));
}
Console.WriteLine("Done.");
Console.ReadKey();
}
}
and the main bulk of the code is unchanged from the example.
public class Knapsack
{
internal static List<double[]> MatchTotal(double theTotal, List<double> subTotals)
{
List<double[]> results = new List<double[]>();
while (subTotals.Contains(theTotal))
{
results.Add(new double[1] { theTotal });
subTotals.Remove(theTotal);
}
// if no subtotals were passed
// or all matched the Total
// return
if (subTotals.Count == 0)
return results;
subTotals.Sort();
double mostNegativeNumber = subTotals[0];
if (mostNegativeNumber > 0)
mostNegativeNumber = 0;
// if there aren't any negative values
// we can remove any values bigger than the total
if (mostNegativeNumber == 0)
subTotals.RemoveAll(d => d > theTotal);
// if there aren't any negative values
// and sum is less than the total no need to look further
if (mostNegativeNumber == 0 && subTotals.Sum() < theTotal)
return results;
// get the combinations for the remaining subTotals
// skip 1 since we already removed subTotals that match
for (int choose = 2; choose <= subTotals.Count; choose++)
{
// get combinations for each length
IEnumerable<IEnumerable<double>> combos = Combination.Combinations(subTotals.AsEnumerable(), choose);
// add combinations where the sum mathces the total to the result list
results.AddRange(from combo in combos
where combo.Sum() == theTotal
select combo.ToArray());
}
return results;
}
}
public static class Combination
{
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int choose)
{
return choose == 0 ? // if choose = 0
new[] { new T[0] } : // return empty Type array
elements.SelectMany((element, i) => // else recursively iterate over array to create combinations
elements.Skip(i + 1).Combinations(choose - 1).Select(combo => (new[] { element }).Concat(combo)));
}
}
I have provided the set of numbers which is in totals which are used to try and calculate the total.
I entered 50.9 as the total and return no results.
The second set of numbers totals2 is a set of numbers which add up to 50.9 and they are a subset of totals. This also fails to return a result for 50.9
For some reason the code is unable to find 50.9 in the master set or in the subset (which itself sums to 50.9).
I can't figure out why it does this, can anyone tell me?

C# How to work-around identical keys in a Dictionary

This program add's values with their other values into a dictionary, all is fine until there are identical keys (var eat(.8) and var extra(.8) with different values. How do i ensure that i can use the right key every time even though they are similar? For example, var example = gigantDictionary[.8] (but i want var edmg value instead of '500' in the code?
var wqat = 1.1; //| index 0
var rat = .2; //| index 1
var eat = .8; //| index 2
var baat = 1.2; //| index 3
var extra = .8; //| index 4
var wqdmg = 120; //| index 0
var rdmg = 60; //| index 1
var edmg = 50; //| index 2
var badmg = 40; //| index 3
var extradmg = 500; //| index 4
List<double> theOneList = new List<double>();
List<double> damageList = new List<double>();
theOneList.Add(wqat);
theOneList.Add(rat);
theOneList.Add(eat);
theOneList.Add(baat);
damageList.Add(wqdmg);
damageList.Add(edmg);
damageList.Add(rdmg);
damageList.Add(badmg);
Dictionary<double, double> gigantDictionary = new Dictionary<double, double>();
for (int i = 0; i < theOneList.Count; i++)
{
gigantDictionary.Add(theOneList[i], damageList[i]);
gigantDictionary.Add(extra, 500); //this is the malignant similar key
}
theOneList.Sort((c, p) => -c.CompareTo(p)); //orders the list
List<double> finalList = new List<double>();
for (int i = 0; i < theOneList.Count; i++)
{
finalList.Add(gigantDictionary[theOneList[i]]); //grabs damage values and add's it to 'finalList'
Console.WriteLine(finalList[i]);
}
So ultimately, i want to order 'theOneList' by descent, in doing so i can get the damages from 'gigantDictionary' and put those into 'finalList', now i have an ordered damage list that i need, but since 2 keys are similar... this is holding me back.
*Edit: Could identical indexes be the key to this? be the bridge? for example, in index 0 i get 1.1 and 120, maybe the answer lies with the identical indexes, i want to get '120' damage from '1.1', notice both have index 0, this might work
They keys aren't "similar" they're "identical". If the keys were just "similar" then, as far as the dictionary is concerned, it's no different than being "completely different". From the point of view of a dictionary items are either equal, or not equal.
For example,
var example = gigantDictionary[.8]
(but i want var edmg value instead of '500' in the code?)
But how should the dictionary possibly know that? How would it know if you actually wanted to get 500?
Do you want to prevent the duplicate keys from being added, and instead always use the first value paired with every key? If so, just check if a key exists before adding a new one.
Do you want to just get all values associated with a key, if there are duplicates? Then have a dictionary where the value is a collection, and add all values associated with that one key to the collection.
Is there actually some way to distinguish the keys so that they're not actually identical? Then do that. With just a double (which is a very bad type to use as a key for a dictionary in the first place, as it's easy for floating point rounding errors to result in similar but different doubles that you consider equivalent) there's no good way to do this, but if your actual key could be different in such a way that distinguishes the two keys, then each could point to a unique value.
Right now you have two separate list for values that must go together. A better approach is to create a structure with the two values and keep a single list.
public class Thing
{
public string Name { get; set; }
public double TheOne { get; set; }
public double Dmg { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Thing> list=new List<Thing>() {
new Thing() { Name = "wq", TheOne = 1.1, Dmg=120 },
new Thing() { Name = "r", TheOne = 0.2, Dmg=60 },
new Thing() { Name = "e", TheOne = 0.8, Dmg=50 },
new Thing() { Name = "ba", TheOne = 1.2, Dmg=40 },
new Thing() { Name = "extra", TheOne = 0.8, Dmg=500 },
};
list.Sort((t1, t2) => t1.TheOne.CompareTo(t2.TheOne));
double[] dmg_list=list.Select((t) => t.Dmg).ToArray();
}
}
Edit 1
A constructor to Thing can be used to assign values with one operation.
public class Thing
{
// Constructor sets all the values
public Thing(string name, double theone, double dmg)
{
this.Name=name;
this.TheOne=theone;
this.Dmg=dmg;
}
public string Name { get; private set; }
public double TheOne { get; private set; }
public double Dmg { get; private set; }
}
class Program
{
static void Main(string[] args)
{
List<Thing> list=new List<Thing>();
list.Add(new Thing("wq", 1.1, 120));
list.Add(new Thing("r", 0.2, 60));
list.Add(new Thing("e", 0.8, 50));
list.Add(new Thing("ba", 1.2, 40));
list.Add(new Thing("extra", 0.8, 500));
list.Sort((t1, t2) => t1.TheOne.CompareTo(t2.TheOne));
double[] dmg_list=list.Select((t) => t.Dmg).ToArray();
}
}

How to count occurences of number stored in file containing multiple delimeters?

This is my input store in file:
50|Carbon|Mercury|P:4;P:00;P:1
90|Oxygen|Mars|P:10;P:4;P:00
90|Serium|Jupiter|P:4;P:16;P:10
85|Hydrogen|Saturn|P:00;P:10;P:4
Now i will take my first row P:4 and then next P:00 and then next like wise and want to count occurence in every other row so expected output will be:
P:4 3(found in 2nd row,3rd row,4th row(last cell))
P:00 2 (found on 2nd row,4th row)
P:1 0 (no occurences are there so)
P:10 1
P:16 0
etc.....
Like wise i would like to print occurence of each and every proportion.
So far i am successfull in splitting row by row and storing in my class file object like this:
public class Planets
{
//My rest fields
public string ProportionConcat { get; set; }
public List<proportion> proportion { get; set; }
}
public class proportion
{
public int Number { get; set; }
}
I have already filled my planet object like below and Finally my List of planet object data is like this:
List<Planets> Planets = new List<Planets>();
Planets[0]:
{
Number:50
name: Carbon
object:Mercury
ProportionConcat:P:4;P:00;P:1
proportion[0]:
{
Number:4
},
proportion[1]:
{
Number:00
},
proportion[2]:
{
Number:1
}
}
Etc...
I know i can loop through and perform search and count but then 2 to 3 loops will be required and code will be little messy so i want some better code to perform this.
Now how do i search each and count every other proportion in my planet List object??
Well, if you have parsed proportions, you can create new struct for output data:
// Class to storage result
public class Values
{
public int Count; // count of proportion entry.
public readonly HashSet<int> Rows = new HashSet<int>(); //list with rows numbers.
/// <summary> Add new proportion</summary>
/// <param name="rowNumber">Number of row, where proportion entries</param>
public void Increment(int rowNumber)
{
++Count; // increase count of proportions entries
Rows.Add(rowNumber); // add number of row, where proportion entry
}
}
And use this code to fill it. I'm not sure it's "messy" and don't see necessity to complicate the code with LINQ. What do you think about it?
var result = new Dictionary<int, Values>(); // create dictionary, where we will storage our results. keys is proportion. values - information about how often this proportion entries and rows, where this proportion entry
for (var i = 0; i < Planets.Count; i++) // we use for instead of foreach for finding row number. i == row number
{
var planet = Planets[i];
foreach (var proportion in planet.proportion)
{
if (!result.ContainsKey(proportion.Number)) // if our result dictionary doesn't contain proportion
result.Add(proportion.Number, new Values()); // we add it to dictionary and initialize our result class for this proportion
result[proportion.Number].Increment(i); // increment count of entries and add row number
}
}
You can use var count = Regex.Matches(lineString, input).Count;. Try this example
var list = new List<string>
{
"50|Carbon|Mercury|P:4;P:00;P:1",
"90|Oxygen|Mars|P:10;P:4;P:00",
"90|Serium|Jupiter|P:4;P:16;P:10",
"85|Hydrogen|Saturn|P:00;P:10;P:4"
};
int totalCount;
var result = CountWords(list, "P:4", out totalCount);
Console.WriteLine("Total Found: {0}", totalCount);
foreach (var foundWords in result)
{
Console.WriteLine(foundWords);
}
public class FoundWords
{
public string LineNumber { get; set; }
public int Found { get; set; }
}
private List<FoundWords> CountWords(List<string> words, string input, out int total)
{
total = 0;
int[] index = {0};
var result = new List<FoundWords>();
foreach (var f in words.Select(word => new FoundWords {Found = Regex.Matches(word, input).Count, LineNumber = "Line Number: " + index[0] + 1}))
{
result.Add(f);
total += f.Found;
index[0]++;
}
return result;
}
I made a DotNetFiddle for you here: https://dotnetfiddle.net/z9QwmD
string raw =
#"50|Carbon|Mercury|P:4;P:00;P:1
90|Oxygen|Mars|P:10;P:4;P:00
90|Serium|Jupiter|P:4;P:16;P:10
85|Hydrogen|Saturn|P:00;P:10;P:4";
string[] splits = raw.Split(
new string[] { "|", ";", "\n" },
StringSplitOptions.None
);
foreach (string p in splits.Where(s => s.ToUpper().StartsWith(("P:"))).Distinct())
{
Console.WriteLine(
string.Format("{0} - {1}",
p,
splits.Count(s => s.ToUpper() == p.ToUpper())
)
);
}
Basically, you can use .Split to split on multiple delimiters at once, it's pretty straightforward. After that, everything is gravy :).
Obviously my code simply outputs the results to the console, but that part is fairly easy to change. Let me know if there's anything you didn't understand.

Creating a two-dimensional array

I am trying to create a two dimensional array and I am getting so confused. I was told by a coworker that I need to create a dictionary within a dictionary for the array list but he couldn't stick around to help me.
I have been able to create the first array that lists the the programs like this
+ project 1
+ project 2
+ project 3
+ project 4
The code that accomplishes this task is below-
var PGList = from x in db.month_mapping
where x.PG_SUB_PROGRAM == SP
select x;
//select x.PG.Distinct().ToArray();
var PGRow = PGList.Select(x => new { x.PG }).Distinct().ToArray();
So that takes care of my vertical array and now I need to add my horizontal array so that I can see the total amount spent in each accounting period. So the final output would look like this but without the dashes of course.
+ program 1-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 2-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 3-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 4-------100---200---300---400---500---600---700---800---900---1000---1100---1200
I have tried to use a foreach to cycle through the accounting periods but it doesn't work. I think I might be on the right track and I was hoping SO could provide some guidance or at the very least a tutorial for me to follow. I have posted the code that I written so far on the second array below. I am using C# and MVC 3. You might notice that their is no dictionary within a dictionary. If my coworker is correct how would I do something like that, I took a look at this question using dictionary as a key in other dictionary but I don't understand how I would use it in this situation.
Dictionary<string, double[]> MonthRow = new Dictionary<string, double[]>();
double[] PGContent = new double[12];
string lastPG = null;
foreach (var item in PGRow)
{
if (lastPG != item.PG)
{
PGContent = new double[12];
}
var MonthList = from x in db.Month_Web
where x.PG == PG
group x by new { x.ACCOUNTING_PERIOD, x.PG, x.Amount } into pggroup
select new { accounting_period = pggroup.Key.ACCOUNTING_PERIOD, amount = pggroup.Sum(x => x.Amount) };
foreach (var P in MonthList)
{
int accounting_period = int.Parse(P.accounting_period) - 1;
PAContent[accounting_period] = (double)P.amount;
MonthRow[item.PG] = PGContent;
lastPG = item.PG;
}
I hope I have clearly explained my issue, please feel free to ask for any clarification needed as I need to solve this problem and will be checking back often. Thanks for your help!
hope this helps.
// sample data
var data = new Dictionary<string, List<int>>();
data.Add("program-1", new List<int>() { 100, 110, 130 });
data.Add("program-2", new List<int>() { 200, 210, 230 });
data.Add("brogram-3", new List<int>() { 300, 310, 330 });
// query data
var newData = (from x in data
where x.Key.Contains("pro")
select x).ToDictionary(v => v.Key, v=>v.Value);
// display selected data
foreach (var kv in newData)
{
Console.Write(kv.Key);
foreach (var val in kv.Value)
{
Console.Write(" ");
Console.Write(val.ToString());
}
Console.WriteLine();
}
output is:
program-1 100 110 130
program-2 200 210 230
Don't try to use anonymous types or LINQ projection to create new data types, especially if you're a beginner, you will just get confused. If you want a specialized data type, define one; e.g.:
public class Account
{
public string Name { get; private set; }
public decimal[] MonthAmount { get; private set; }
readonly int maxMonths = 12;
public Account(string name, ICollection<decimal> monthAmounts)
{
if (name == null)
throw new ArgumentNullException("name");
if (monthAmounts == null)
throw new ArgumentNullException("monthAmounts");
if (monthAmounts.Count > maxMonths)
throw new ArgumentOutOfRangeException(string.Format(" monthAmounts must be <= {0}", maxMonths));
this.Name = name;
this.MonthAmount = new decimal[maxMonths];
int i = 0;
foreach (decimal d in monthAmounts)
{
this.MonthAmount[i] = d;
i++;
}
}
}
Use instances of this type directly, you do not have to convert them to arrays, dictionaries, lists, or anything else:
var accountPeriods = new List<Account>();
accountPeriods.Add(new Account("program-1", new decimal[] { 1, 2, 3, 4 }));
You can use LINQ or whatever to query or alter instances of your new type:
foreach (Account a in accountPeriods)
foreach (decimal d in a.MonthAmount)
DoSomethingWith(d);
That should be enough to get you started.
I want to thank #Ray Cheng and #Dour High Arch for their help but I have figured out another way to accomplish this task and I wanted to post my code so that the next person that is having the same trouble can figure out their problem faster.
Above I split my code into more managable sections to explain my problem as clearly as I could and the code below has all those parts combined so you can see the big picture. This code returns an array that contains the program and the amounts for every month.
public virtual ActionResult getAjaxPGs(string SP = null)
{
if (SP != null)
{
var PGList = from x in db.month_mapping
where x.PG_SUB_PROGRAM == SP
select x;
var PGRow = PGList.Select(x => new { x.PG }).Distinct().ToArray();
float[] PGContent = new float[12];
Dictionary<string,float[]> MonthRow = new Dictionary<string, float[]>();
foreach (var item in PGRow)
{
PGContent = new float[12];
var MonthList = from x in db.month_Web
where x.PG == item.PG
group x by new { x.ACCOUNTING_PERIOD, x.PG, x.Amount } into pggroup
select new { accounting_period = pggroup.Key.ACCOUNTING_PERIOD, amount = pggroup.Sum(x => x.Amount) };
foreach (var mon in MonthList)
{
int accounting_period = int.Parse(mon.accounting_period) - 1;
PGContent[accounting_period] = (float)mon.amount/1000000;
}
MonthRow[item.PG] = PGContent;
}
return Json(MonthRow, JsonRequestBehavior.AllowGet);
}
return View();
}
This code worked great for me since I am pulling from a Linq to SQL query instead of adding data directly into the code. My problems stemmed from mainly putting the data pulls outside of the foreach loops so it only pulled 1 piece of data from the SQL instead of all twelve months. I hope this helps some one else who is trying to pull data in from SQL data sources into multidimensional arrays.

Categories