I am pulling form values from a loosely bound razor form. The reason I am not using strongly bound model is that the payment value fields and categories are dynamic.
The form collection array reaching the controller is as below:
Payment {"1":"120","4":"23","6":"12","8":"120","9":"100"}
I need to split the array like (When PayCatId =1, Pay =120) (When PayCatId =4, Pay=23) etc..
string [] pa =collection["Payment"].Split(char.Parse(","));
string [] pc =collection.AllKeys["Payment"].Split(char.Parse(","));
Then I am trying to save to database using the logic below;
for (var i = 0; i < pa.Length; i++)
{
payment.Pay = Convert.ToDecimal(pa[i]);
payment.PayCatId = Convert.ToInt32(pa[i]); (added:how do i get this value from pair?)
payment.PayDate = DateTime.Now;
db.Payments.Add(payment);
db.SaveChanges();
}
Removed the Error bit as I have been enlightened that that approach is not applicable
I also want to know if this is the right and reliable approach to achieve this objective.
Just loop through two steps at a time instead of one
for (var i = 0; i < pa.Length; i+=2)
{
payment.Pay = Convert.ToDecimal(pa[i]);
payment.PayCatId = Convert.ToInt32(pa[i+1]);
payment.PayDate = DateTime.Now;
db.Payments.Add(payment);
db.SaveChanges();
}
You can use a combination of Split, Select and ToDictionary to do this, see the code:
var srt = "\"1\":\"120\",\"4\":\"23\",\"6\":\"12\",\"8\":\"120\",\"9\":\"100\"";
srt.Split(',')
.Select(x => x.Split(':'))
.ToDictionary(x => int.Parse(x[0].Replace("\"","")), x => int.Parse(x[1].Replace("\"","")))
/*
Output:
Dictionary<int, int>(5)
{
{ 1, 120 },
{ 4, 23 },
{ 6, 12 },
{ 8, 120 },
{ 9, 100 }
}
*/
Related
I have the below code in which i branch for each sample in a dictionary , is there a way either by using LINQ or any other method in which i can avoid branching -> may be a functional approach
Dictionary<string, int> samples = new Dictionary<string, int>()
{
{"a", 1},
{"aa", 2},
{"b", 1},
{"bb", 3}
};
foreach (var sample in samples)
{
if (sample.Value ==)
{
Console.WriteLine("sample passed");
}
else if (sample.Value == 2)
{
Console.WriteLine("sample isolated");
}
else if (sample.Value == 3)
{
Console.WriteLine("sample biased");
}
}
UPD
What if i have other type of comprasion:
foreach (var sample in samples)
{
if (sample.Value <= 1)
{
Console.WriteLine("sample passed");
}
else if (sample.Value <= 2)
{
Console.WriteLine("sample isolated");
}
else if (sample.Value <= 3)
{
Console.WriteLine("sample biased");
}
}
One option would be to create a list of Actions that you wish to perform, then execute them based on the index. This way your methods can be quite varied. If you need to perform very similar actions for each option, then storing a list of values would be a better than storing Actions.
List<Action> functions = new List<Action>();
functions.Add(() => Console.WriteLine("sample passed"));
functions.Add(() => Console.WriteLine("sample isolated"));
functions.Add(() => Console.WriteLine("sample biased"));
foreach (var sample in samples)
{
Action actionToExecute = functions[sample.Value - 1];
actionToExectute();
}
If you wanted to use a dictionary as your comment implies:
Dictionary<int, Action> functions = new Dictionary<int, Action>();
functions.Add(1, () => Console.WriteLine("sample passed"));
functions.Add(2, () => Console.WriteLine("sample isolated"));
functions.Add(3, () => Console.WriteLine("sample biased"));
foreach (var sample in samples)
{
Action actionToExecute = functions[sample.Value];
actionToExectute();
}
For this concrete case you can introduce another map(Dictionary or an array, as I did):
Dictionary<string, int> samples = new Dictionary<string, int>()
{
{"a", 1},
{"aa", 2},
{"b", 1},
{"bb", 3}
};
var map = new []
{
"sample passed",
"sample isolated",
"sample biased"
};
foreach (var sample in samples)
{
Console.WriteLine(map[sample.Value - 1]);
}
As for actual code it highly depends on usecases and how you want to handle faulty situations.
UPD
It seems that if you will be using dictionary for your map there still will be some branching, but if you will not have misses branch prediction should take care of it.
So you have a Dictionary<string, int>. Every item in the dictionary is a KeyValuePair<string, int>. I assume that the string is the name of the sample (identifier), and the int is a number that says something about the sample:
if the number equals 0 or 1, the sample is qualified as Passed;
if the number equals 2, then you call it Isolated
if the number equals 3, then you call it Biased.
All higher numbers are not interesting for you.
You want to group the samples in Passed / Isolated / Biased samples.
Whenever you have a sequence of similar items and you want to make groups of items, where every element has something in common with the other elements in the group, consider using one of the overloads of Enumerable.GroupBy
Let's first define an enum to hold your qualifications, and a method that converts the integer value of the sample into the enum:
enum SampleQualification
{
Passed,
Isolated,
Biased,
}
SampleQualification FromNumber(int number)
{
switch (number)
{
case 2:
return SampleQualification.Isolated;
case 3:
return SampleQualification.Biased;
default:
return SampleQualification.Passed;
}
}
Ok, so you have your dictionary of samples, where every key is a name of the sample and the value is a number that can be converted to a SampleQualification.
Dictionary<string, int> samples = ...
var qualifiedSamples = samples // implements IEnumerable<KeyValuePair<string, int>>
// keep only samples with Value 0..3
.Where(sample => 0 <= sample.Value && sample.Value <= 3)
// Decide where the sample is Passed / Isolated / Biased
.Select(sample => new
{
Qualification = FromNumber(sample.Value)
Name = sample.Key, // the name of the sample
Number = sample.Value,
})
// Make groups of Samples with same Qualification:
.GroupBy(
// KeySelector: make groups with same qualification:
sample => sample.Qualification,
// ResultSelector: take the qualification, and all samples with this qualification
// to make one new:
(qualification, samplesWithThisQualification) => new
{
Qualification = qualification,
Samples = samplesWithThisQualification.Select(sample => new
{
Name = sample.Name,
Number = sample.Number,
})
.ToList(),
});
The result is a sequence of items. Where every item has a property Qualification, which holds Passed / Isolated / Biased. Every item also has a list of samples that have this qualification.
// Process Result
foreach (var qualifiedSample in qualifiedSamples)
{
Console.WriteLine("All samples with qualification " + qualifiedSample.Qualification);
foreach (var sample in qualifiedSample.Samples)
{
Console.WriteLine({0} - {1}, sample.Name, sample.Value);
}
}
I have a JSON file that contains about 20k lines of code that has to be read, sorted and saved into a database. I've written code for it and it works the way it's suppose to but my issue is that it takes about 10 minutes. Therefor I wonder if someone has any ideas what can be done to enhance the performance?
Json:
{
"Number": 123456,
"Area": "NE01"
},
{
"Number": 123457,
"Area": "NE01"
},
and so forth....
C#:
dynamic json = JsonConvert.DeserializeObject(File.ReadAllText(path, Encoding.UTF8));
foreach (var obj in json)
{
if (obj.Area == "NE01")
{
var o = new object
{
Number = obj.Number,
};
db.Entity.Add(obj);
continue;
}
if (obj.Area == "NE02")
{
var o = new object
{
Number = obj.Number,
};
db.Entity.Add(obj);
continue;
}
if (obj.Area == "NE03")
{
var o= new object
{
Number = obj.Number,
};
db.Entity.Add(obj);
continue;
}
if ( obj.Area== "NE04")
{
var o = new object
{
Number = obj.Number
};
db.Entity.Add(obj);
continue;
}
}
db.SaveChanges();
To make it clearer, area has four different values. Depending on the value the number will have a foreign key that points to the area. Unfortunately I'm not allowed to change anything in the underlying database.
Let me know if I have to provide further information.
Using EntityFramework.Utilities you can use Bulk Insert which should speed up insertions.
Something like:
public class Data
{
public int Number { get; set; }
public string Area { get; set; }
}
var objects = JsonConvert.DeserializeObject<List<Data>>(File.ReadAllText(path, Encoding.UTF8))
.Select(d => new object { Number = d.Number })
.ToList();
EFBatchOperation.For(db, db.Entity).InsertAll(objects);
Disclaimer: Code not tested.
Only by using AddRange I was able to decrease the time to just over one minute. Which is good enough for my purpose.
I've got a 2d array of objects that I am passing into my seriesSet for creating a column graph, one column is a percentage, which is what I want to see plotted on the graph where the other I only want to show in the tooltip.
Can any of you highcharts geniuses think of a way I can do this? e.g. it looks something like this
{ 100, 20 }
{ 100, 20 }
{ 80 , 16 }
{ 80 , 16 }
{ 40 , 8 }
{ 40 , 8 }
{ 20 , 4 }
...
I know now how to refer to each in the SetToolTip Formatter using 'this.x/this.y/this.point.x' which is progress since I was stuck for a quite a while trying to do that. But now I only want the first set (100, 80, 40, 20) to be used to draw the graph where the second set should only be used for the tooltip.
Coming up on 2 weeks puzzling over this work item so any help is appreciated
edit:
to clarify, the graph draws as I want it to when I pass in a 1D object array consisting of the percentages only, then breaks when I include the second set (the counts)
so I am setting my seriesData like so:
List<DotNet.Highcharts.Options.Series> seriesSet = new List<DotNet.Highcharts.Options.Series>();
seriesSet.Add(new DotNet.Highcharts.Options.Series
{
Type = chartType,
Name = "ExampleArray",
Data = new DotNet.Highcharts.Helpers.Data(ExampleArray),
Color = tarColor,
PlotOptionsColumn = new DotNet.Highcharts.Options.PlotOptionsColumn
{
PointPadding = -0.1
},
});
where example array is composed of the list of numbers above.
I'm not sure I understood correctly but if what you want is get a collection of all the first items in this collection and then another one for the second then:
// Remove any item that doesn't have at least these two wanted values
items = items.Where(item => item.Count >= 1);
// Select the sub-collection you need
var percentages = items.Select(item => item[0]);
var tooltips = items.Select(item => item[1]);
From looking around a bit (like here) it seems you should pass a Object[] to the Data so replace ExampleArray with:
items.Select(item => (object)item[0]);
seriesSet.Add(new DotNet.Highcharts.Options.Series
{
Type = chartType,
//Name = "Targeted_" + Convert.ToString(tarCount.Count()),
Name = "Targeted",
Data = nnew DotNet.Highcharts.Helpers.Data((object[])targeted.Cast<object[]>()
.Select(item => (object)item[0])),
Color = tarColor,
PlotOptionsColumn = new DotNet.Highcharts.Options.PlotOptionsColumn
{
PointPadding = -0.1
},
});
i am a total noob when it comes to c#
i migrated to a new platform which uses c#, trying to migrate trading rules to new design.
i need to do this for algorithm
here is the question
i have keys array a,b,c,d
i have value set1 3,8,9,10
another value set2 77,89,100,76
these values are related to each other , (a) has values 3,77 and so on
what i need is , i need to filter with value set 2 , for example only values more than 80 then (probably create a new list with the remaining rows) , from the remaining list i need to get the keyname with highest set1 value
i tried it with this probably very bad way,
Array.Sort on one dimensional array value set1, take value[3]
- if this equals 3 then (if value set1 >80 valuefound else take value[2] and repeat
can you show me an easier way , please take my inexperience into account and include as more informatioan and code as possible
You should really start learning more about Linq. C# has very powerful functional-like features that you can do these things really easily with. It's actually really fun :)
Basically, this code does what you want with 3 lines.
var set1 = new[] {3, 8, 9, 10};
var set2 = new[] {77, 89, 100, 76};
var maxFromSet1 = set1
.Zip(set2, (fromSet1, fromSet2) => new {FromSet1 = fromSet1, FromSet2 = fromSet2}) //Match the sets to one another
.Where(zipped => zipped.FromSet2 > 80) // Filter by value
.Max(zipped => zipped.FromSet1); //Gets max
Another way of doing what you want would be to use a Dictionary which holds keys and values together instead of having them in different arrays.
Dictionary<string, int[]> dic = new Dictionary<string, int[]>()
{
{ "a", new[] { 3, 77 } },
{ "b", new[] { 8, 89 } },
{ "c", new[] { 9, 100 } },
{ "d", new[] { 10, 76 } }
};
Then using LINQ you can retrieve the key really easily
string key = dic.Where(x => x.Value[1] > 80) // Filter by second value
.OrderByDescending(x => x.Value[0]) // Order by first value
.First() // Get the max value
.Key; // Get the matching key
I have a list function on a console application on C#. This list function has different items where they look something like 'matt,5' 'matt,7' 'jack,4' 'jack,8' etc...
I want to be able to combine all of the names where I only see their name written once but the number after them are averaged out so it would be like 'jack,5+7/2' which would then display as 'jack,6'.
So far I have this...
currentFileReader = new StreamReader(file);
List<string> AverageList = new List<string>();
while (!currentFileReader.EndOfStream)
{
string text = currentFileReader.ReadLine();
AverageList.Add(text.ToString());
}
AverageList.GroupBy(n => n).Any(c => c.Count() > 1);
Not really sure where to go from here.
What you need is to Split your each string item on , and then group by first element of the returned array and average second element of the array (after parsing it to int) something like:
List<string> AverageList = new List<string> { "matt,5", "matt,7", "jack,4", "jack,8" };
var query = AverageList.Select(s => s.Split(','))
.GroupBy(sp => sp[0])
.Select(grp =>
new
{
Name = grp.Key,
Avg = grp.Average(t=> int.Parse(t[1])),
});
foreach (var item in query)
{
Console.WriteLine("Name: {0}, Avg: {1}", item.Name, item.Avg);
}
and it will give you:
Name: matt, Avg: 6
Name: jack, Avg: 6
But, a better option would be to use a class with Name and Score properties instead of comma separated string values.
(The code above doesn't check for invalid input values).
Firstly you will want to populate your unformatted data into a List, as you can see I called it rawScores. You could then Split each line by the comma delimiting them. You can then check to see if an existing person is in your Dictionary and add his score to it, or if not create a new person.
After that you would simply have to generate the Average of the List.
Hope this helps!
var scores = new Dictionary<string, List<int>>();
var rawScores = new List<string>();
rawScores.ForEach(raw =>
{
var split = raw.Split(',');
if (scores.Keys.All(a => a != split[0]))
{
scores.Add(split[0], new List<int> {Convert.ToInt32(split[1])});
}
else
{
var existing = scores.FirstOrDefault(f => f.Key == split[0]);
existing.Value.Add(Convert.ToInt32(split[1]));
}
});