Related
I have a dictionary of lists like so:
var dictOfLists = new Dictionary<string, List<string>>
{
["foo"] = new List<string>{ "a", "b", "c" },
["bar"] = new List<string>{ "d" },
["baz"] = new List<string>{ "e", "a" }
}
I want to convert this to a list of unique dictionaries like so:
var listOfUniqDicts = new List<Dictionary<string, string>>
{
new Dictionary<string, string> {["foo"] = "a", ["bar"] = "d", ["baz"] = "e" },
new Dictionary<string, string> {["foo"] = "a", ["bar"] = "d", ["baz"] = "a" },
new Dictionary<string, string> {["foo"] = "b", ["bar"] = "d", ["baz"] = "e" },
new Dictionary<string, string> {["foo"] = "b", ["bar"] = "d", ["baz"] = "a" },
new Dictionary<string, string> {["foo"] = "c", ["bar"] = "d", ["baz"] = "e" },
new Dictionary<string, string> {["foo"] = "c", ["bar"] = "d", ["baz"] = "a" },
}
(As you can see, in the above list, each of the dictionaries represents a unique combination of values, whose keys map to the respective keys of the initial dictionary.)
Is there a clean algorithm to do this with an arbitrary dictionary of the above type?
It's like constructing a thruth table:
In the first column you write true, false, true, false, ...
In the second column you repeat each item 2 times: true, true false, false, true, true, false, false, ...
In the nth column you repeat each item 2^(n-1) times
This problem is a generalized version (try in dotnetfiddle):
private Dictionary<TKey, TValue>[] BuildDictionary<TKey, TValue>(Dictionary<TKey, List<TValue>> dict) where TKey : notnull
{
// Calculating the size of the result (product of the length of each entry)
int[] lengths = dict.Select(d => d.Value.Count()).ToArray();
int size = lengths.Aggregate(1, (prev, val) => prev * val); // reduce in JavaScript
var result = new Dictionary<TKey, TValue>[size];
for (int i = 0; i < result.Length; i++) { result[i] = new Dictionary<TKey, TValue>(); }
int repeats = 1;
foreach (var d in dict)
{
var key = d.Key;
for (int row = 0; row < size;)
for (int i = 0; i < repeats; i++)
foreach (var val in d.Value)
{
result[row].Add(key, val);
row++;
}
repeats *= d.Value.Count;
}
return result;
}
Result:
[
{"foo":"a","bar":"d","baz":"e"},
{"foo":"b","bar":"d","baz":"a"},
{"foo":"c","bar":"d","baz":"e"},
{"foo":"a","bar":"d","baz":"a"},
{"foo":"b","bar":"d","baz":"e"},
{"foo":"c","bar":"d","baz":"a"}
]
How do I create a method setOutput() that calculates and sets the max output of each item (a,b,c,d) in Formula by multiplying the percentage in Formula to the amt in Item through its common id? I think I worked out the logic but I don't know how to use the id to reference values for calculation.
I.e. how do I reference values by their id within arrays in a nested foreach loop?
Class Item {
public string id { get;set }
public double? amt { get;set } }
Class Formula {
public string id { get;set }
public double? percent { get;set }
public double? output { get;set } }
Item[] inventory = {
new Item { id = "a", amt = 111.1},
new Item { id = "b", amt = 222.2},
new Item { id = "c", amt = 333.3,
new Item { id = "d", amt = 400.4} }
Formula[] formulas = { new Formula {
{id = "a", percent=25.0, output = null},
{id = "b", percent=25.0, output = null},
{id = "c", percent=25.0, output = null},
{id = "d", percent=25.0, output = null}; },
new Formula {
{id = "a", percent=20.0, output = null};
{id = "b", percent=20.0, output = null};
{id = "c", percent=60.0, output = null};
{id = "d", percent= 0.0, output = null}; },
new Formula {
{id = "a", percent=30.0, output = null};
{id = "b", percent=30.0, output = null};
{id = "c", percent=20.0, output = null};
{id = "d", percent=20.0} output = null}; } }
setOutput(Item[] inventory, Formula[] formulas)
{
// Loop through each Formula in formulas
// Loop through each Item in
// Item.amt * Formula.percentage/100 = Formula.amt
foreach (var Formula in formulas) //
{
foreach (Item item in Raw)
{
(id="a") Raw.amt* Formula.percent/100 = Formula.output
(id="b") Raw.amt* Formula.percent/100 = Formula.output
(id="c") Raw.amt* Formula.percent/100 = Formula.output
(id="d") Raw.amt* Formula.percent/100 = Formula.output
}
}
}
Hi assuming if your Item array has Items with unique id's something like below in your code
Item[] inventory = {new Item { id = "a", amt = 111.1},
new Item { id = "b", amt = 222.2},
new Item { id = "c", amt = 333.3 },
new Item { id = "d", amt = 400.4} };
Formula[] formulas = { new Formula {id = "a", percent=25.0, output = null} ,
new Formula {id = "b", percent=25.0, output = null} ,
new Formula {id = "c", percent=25.0, output = null},
new Formula {id = "d", percent=25.0, output = null} };
Then in your setouput function you could do something like below to set your output property in formulas array
formulas.ToList().ForEach(var => var.output = var.percent * inventory.First(var1 => var1.id == var.id).amt / 100);
A better approach is to make formulas data type from Formula[] to List
Assuming you meant for your formulas to be List<Formula>[] so you could have lists of Formulas, converting the inventory of Items to a Dictionary makes it easy:
void setOutput(Item[] inventory, List<Formula>[] formulas) {
var Raw = inventory.ToDictionary(i => i.id, i => i.amt);
foreach (var FormulaSet in formulas)
foreach (var formula in FormulaSet)
formula.output = Raw[formula.id] * formula.percent / 100;
}
I have data table having rows like
ID Name
2 A
4 B
3 C
5 D
1 E
List order = new List() { "1", "3", "2", "5", "4" }
--------------order by list-----------------
ID Name
1 E
3 C
2 A
5 D
4 B
can anyone help to implement this.. I am using DataTable in Winforms.
Another solution to the already given ones, would be to loop your order list and then sort your source list.
// source list
List<Foo> lSource = new List<Foo>() {
new Foo() { ID = 2, Name = "A" },
new Foo() { ID = 4, Name = "B" },
new Foo() { ID = 3, Name = "C" },
new Foo() { ID = 5, Name = "D" },
new Foo() { ID = 1, Name = "E" },
};
// order list
List<int> order = new List<int>() { 1, 3, 2, 5, 4 };
// loop order list and sort source list
order.ForEach(x =>
{
lSource = lSource.OrderBy(g => g.ID == x).ToList();
});
// set datasource
dataGridView1.DataSource = lSource;
I just added a class Foo containing an int ID and a string Name, because you didn't share your whole code.
I think you can join your order and your datatable with AsEnumerable method and on on part you can equalize both of them and select rows, then you can generate a DataTable from that query with CopyToDataTable method.
var dt = new DataTable();
var dc = new DataColumn() { ColumnName = "ID", DataType = typeof(string) };
dt.Columns.Add(dc);
dc = new DataColumn() { ColumnName = "Name", DataType = typeof(string) };
dt.Columns.Add(dc);
dt.Rows.Add(new object[] { "2", "A" });
dt.Rows.Add(new object[] { "4", "B" });
dt.Rows.Add(new object[] { "3", "C" });
dt.Rows.Add(new object[] { "5", "D" });
dt.Rows.Add(new object[] { "1", "E" });
List<string> order = new List<string>() { "1", "3", "2", "5", "4" };
var query = from item in order
join row in dt.AsEnumerable() on item equals row.Field<string>("ID")
select row;
var result = query.CopyToDataTable();
result will be;
I'm not sure this is the best way or not but this seems to fit with your case.
You can join both lists (the one with items and the one with sorted id's) and then select the items:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var list = new List<Item>{
new Item { Id = 2, Text = "A" },
new Item { Id = 4, Text = "B" },
new Item { Id = 3, Text = "C" },
new Item { Id = 5, Text = "D" },
new Item { Id = 1, Text = "E" }
};
var sortorder = new List<int> { 1, 3, 2, 5, 4 };
var sortedlist = sortorder.Join(list, x => x, y => y.Id, (x,y) => y);
foreach(var item in sortedlist)
Console.WriteLine("{0} {1}", item.Id, item.Text);
}
}
I have a 2-dimensional array like below.
If I want to sum all time and score of rows of Each ID that start from lesson startpoint=E and ends by lesson endspoint=I. In the case below, for ID 1 it become (time=190+195+200=585 and score=3+3+4=10) and ID 3 that (time=190+210+160=560 and score=5+4+4=13).
I already came up with following loop.
ID Lesson Time Score
1 C 165 4
1 E 190 3
1 H 195 3
1 I 200 4
2 A 100 2
2 B 150 5
2 D 210 2
2 E 110 4
3 D 130 5
3 E 190 5
3 H 210 4
3 I 160 4
3 J 110 4
4 E 120 3
4 H 150 4
4 J 170 4
-
for (int i=0; i<SizeofDataGrid;i++)//save datatable into array
{
for (int j=0; j<4;j++)
{
FILTERED[i,j]=Convert.ToString(interaction.Rows[i][j]);
}
}
for (int i = 0; i < FILTERED.GetLength(0); i++)
{
string UNIQUEID = UniqueUserId[i];
for (int j = 0; j < SizeofDataGrid; j++)
{
if (UNIQUEID == FILTERED[j,0])// match ID
{
for (int x = 0; x < SizeofDataGrid; x++)
{
if (startpoint == FILTERED[j, 1]) //Match start Lesson
{
TotalTime[1, i] = TotalTime[1, i] + Convert.ToUInt16(FILTERED[j, 2]);
TotalTime[0, i] = i;
TotalScore[1, i] = TotalScore[1, i] + Convert.ToUInt16(FILTERED[j, 3]);
TotalScore[0, i] = i;
}
}
}
}
}
Try this:
// this method does the calculations then puts the values in the totalScore and totaltime variables
public void DoCalculations(string[,] data, string id, out int totalScore, out int totalTime)
{
totalTime = 0;
totalScore = 0;
for (int i = 0; i < data.GetLength(0); i++)
{
if (data[i, 0] != id) continue;
// modify this string to contain the lessons between E and I
if (!"EFGHI".Contains(data[i, 1])) continue;
// do proper error checking unless you're sure they'll always be integers
totalTime += int.Parse(data[i, 2]);
totalScore += int.Parse(data[i, 3]);
}
}
And this is the usage example:
string[,] data = new string[,]
{
{"1", "C", "165", "4"},
{"1", "E", "190", "3"},
{"1", "H", "195", "3"},
{"1", "I", "200", "4"},
{"2", "A", "100", "2"},
{"2", "B", "150", "5"},
{"2", "D", "210", "2"},
{"2", "E", "110", "4"},
{"3", "D", "130", "5"},
{"3", "E", "190", "5"},
{"3", "H", "210", "4"},
{"3", "I", "160", "4"},
{"3", "J", "110", "4"},
{"4", "E", "120", "3"},
{"4", "H", "150", "4"},
{"4", "J", "170", "4"}
};
// will store the total score
int totalScore = 0;
// will store the total time
int totalTime = 0;
// calling the function for the id=3
DoCalculations(data, "3", out totalScore, out totalTime);
Console.WriteLine(totalTime); // will output: 560
Console.WriteLine(totalScore); // will output: 13
Do you know about the break statement? You should be able to create a conditional statement that checks for your condition, then put break; at the bottom of it and you will exit the loop.
I would try to avoid using string[,] FILTERED to store your data. You really can easily create strong data types instead. However, if you must use it then I would convert to a strong data type and then do the calculations.
So, starting with the raw data:
string[,] FILTERED = new string[16, 4]
{
{ "1", "C", "165", "4" },
{ "1", "E", "190", "3" },
{ "1", "H", "195", "3" },
{ "1", "I", "200", "4" },
{ "2", "A", "100", "2" },
{ "2", "B", "150", "5" },
{ "2", "D", "210", "2" },
{ "2", "E", "110", "4" },
{ "3", "D", "130", "5" },
{ "3", "E", "190", "5" },
{ "3", "H", "210", "4" },
{ "3", "I", "160", "4" },
{ "3", "J", "110", "4" },
{ "4", "E", "120", "3" },
{ "4", "H", "150", "4" },
{ "4", "J", "170", "4" },
};
I would first turn this into strongly-type data:
var data =
FILTERED
.Cast<string>()
.Select((x, n) => new { x, n })
.GroupBy(xn => xn.n / 4, xn => xn.x)
.Select(xs => new
{
ID = int.Parse(xs.ElementAt(0)),
Lession = xs.ElementAt(1),
Time = int.Parse(xs.ElementAt(2)),
Score = int.Parse(xs.ElementAt(3)),
});
This gives:
I would then do this query:
var result =
data
.GroupBy(x => x.ID)
.Select(xs => new
{
ID = xs.Key,
Lessions = xs
.Where(x => x.Lession.Intersect("EFGHI").Any())
.ToArray(),
})
.Select(x => new
{
x.ID,
Time = x.Lessions.Sum(y => y.Time),
Score = x.Lessions.Sum(y => y.Score),
})
.ToArray();
That gives me this result:
Now, just as an alternative to mucking around with the intermediate FILTERED array you could just get the data directly from the DataTable like this:
var data =
from row in interaction.Rows.Cast<DataRow>()
select new
{
ID = row.Field<int>(0),
Lession = row.Field<string>(1),
Time = row.Field<int>(2),
Score = row.Field<int>(3),
};
I have a generic list such as below,
var steps = new List<Step>
{
new Step { From = "A", To = "D", Quantity = 0 },
new Step { From = "D", To = "J", Quantity = 0 },
new Step { From = "J", To = "T", Quantity = 0 },
new Step { From = "D", To = "K", Quantity = 0 },
new Step { From = "K", To = "T", Quantity = 0 },
new Step { From = "E", To = "K", Quantity = 0 },
new Step { From = "A", To = "E", Quantity = 0 },
new Step { From = "B", To = "E", Quantity = 0 },
new Step { From = "E", To = "L", Quantity = 5 },
new Step { From = "B", To = "F", Quantity = 5 },
new Step { From = "B", To = "G", Quantity = 5 },
new Step { From = "F", To = "I", Quantity = 5 },
new Step { From = "G", To = "I", Quantity = 5 },
new Step { From = "C", To = "H", Quantity = 0 },
new Step { From = "H", To = "Z", Quantity = 0 },
new Step { From = "H", To = "Y", Quantity = 0 },
new Step { From = "H", To = "X", Quantity = 5 },
new Step { From = "X", To = "I", Quantity = 5 },
new Step { From = "I", To = "V", Quantity = 5 },
new Step { From = "L", To = "V", Quantity = 5 },
new Step { From = "Y", To = "V", Quantity = 5 },
new Step { From = "Y", To = "M", Quantity = 0 },
new Step { From = "Z", To = "M", Quantity = 0 },
new Step { From = "Z", To = "N", Quantity = 0 },
new Step { From = "M", To = "O", Quantity = 0 },
};
I would like to remove items from the list. It will start first item of the list. It will remove items until next quantity of item is greater then zero.
It should do the same job reverse.
It is like string.Trim('0').
My result should be like below :
var steps = new List<Step>
{
new Step { From = "E", To = "L", Quantity = 5 },
new Step { From = "B", To = "F", Quantity = 5 },
new Step { From = "B", To = "G", Quantity = 5 },
new Step { From = "F", To = "I", Quantity = 5 },
new Step { From = "G", To = "I", Quantity = 5 },
new Step { From = "C", To = "H", Quantity = 0 },
new Step { From = "H", To = "Z", Quantity = 0 },
new Step { From = "H", To = "Y", Quantity = 0 },
new Step { From = "H", To = "X", Quantity = 5 },
new Step { From = "X", To = "I", Quantity = 5 },
new Step { From = "I", To = "V", Quantity = 5 },
new Step { From = "L", To = "V", Quantity = 5 },
new Step { From = "Y", To = "V", Quantity = 5 },
};
You want something like this:
steps = steps.SkipWhile(s => s.Quantity == 0).Reverse()
.SkipWhile(s => s.Quantity == 0).Reverse()
.ToList();
This would be the "simple" solution I guess.
It would be faster to search for the bounds of the desired range of steps first and then get that using GetRange.
I think, the best option is reassign step to new list
list = list.SkipWhile(s => s.Quantity == 0).ToList();
UPD: Yeah, my fault, I made TrimStart only. Was inattention.
This should do the trick:
steps = steps
.SkipWhile(step => step.Quantity == 0)
.TakeWhile((step, index) =>
steps.Skip(index).Any(nextSteps => nextSteps.Quantity != 0))
.ToList();
The SkipWhile first skips all steps with quantity=0, then it takes all steps so long as there are following steps with a quantity != 0.
Whilst the simple solution would be to revere the list twice that could get expensive for large lists.
list = list.SkipWhile(s => s.Quantity == 0).Reverse()
.SkipWhile( s => s.Quantity == 0).Reverse().ToList();
A more efficient solution would be to define an extension method like this:
public static IEnumerable TrimLast(IEnumerable<T> this, Func<T, Bool> cond) {
foreach (var item in this) {
if (!cond(item)) {
foreach (var storeditem in store) {
yield return storeditem;
}
store.Clear;
yield return item;
} else {
store.Add(item);
}
}
}
then call it: list = list.SkipWhile(s => s.Quantity == 0).TrimLast(s => s.Quantity == 0).ToList();
This has the advantage of never storing more than the longest consecutive sequence of zeros unlike the reverse solution which needs to store the whole list twice.