How to run a different query depending on case switch statement? (C#) - c#

Here's my code which works up to a point:
var Data = File.ReadAllLines(FilePath).Select(line => line.Split('\t')).ToArray();
int caseSwitch = 0;
if (radioButton1.Checked == true)
{
caseSwitch = 1;
}
else if (radioButton2.Checked == true)
{
caseSwitch = 2;
}
else if (radioButton3.Checked == true)
{
caseSwitch = 3;
}
var query = from x in Data
let sw = caseSwitch
select
sw == 1 ? new { Name = x[6], Age = x[2], Date = x[4], Score = x[7] }
: sw == 2 ? new { Name = x[9], Age = x[1], Date = x[0], Score = x[3] }
: sw == 3 ? new { Name = x[5], Age = x[8], Date = x[2], Score = x[1] }
: null;
It seems the code stops working when I have up to 8 case switch scenarios...the error I seem to get once I have 8 scenarios is "Index was outside the bounds of the array" whilst the ": null;" statement is highlighted in yellow. What am I doing wrong?

"Index was outside the bounds of the array" would indicate that you have a bad index value for x in one (or more) of your cases - the number of switch statements would not cause that error.
Post the line in your select that causes the error.

A better way to do this might be to have the query do everything but the select itself. You can then do your switch like this:
IQueryable<whatever> newQuery;
switch (caseSwitch)
{
case 1: newQuery = query.Select(i => new { Name = i[9], ... }); break;
case 2: newQuery = query.Select(i => new { Name = i[6], ... }); break;
}
Note that you can't use an anonymous type, but that shouldn't be a problem here. This way, you only send the query you actually want, rather than doing a case on the server.

You do not need a conditional statement there at all - since the structure of your query does not change, all you need is a table providing the indexes for Name, Age, Date, and Score columns:
class ColumnIndex {
int NameIndex {get;set;}
int AgeIndex {get;set;}
int DateIndex {get;set;}
int ScoreIndex {get;set;}
}
private static readonly ColumnIndex[] ColIndex = new[] {
new ColumnIndex {NameIndex = 6, AgeIndex = 2, DateIndex = 4, ScoreIndex = 7}
, new ColumnIndex {NameIndex = 9, AgeIndex = 1, DateIndex = 0, ScoreIndex = 3}
, new ColumnIndex {NameIndex = 5, AgeIndex = 8, DateIndex = 2, ScoreIndex = 1}
, ...
};
...
int caseSwitch = -1;
if (radioButton1.Checked == true)
{
caseSwitch = 0;
}
else if (radioButton2.Checked == true)
{
caseSwitch = 1;
}
else if (radioButton3.Checked == true)
{
caseSwitch = 2;
}
var query = from x in Data
select new {
Name = x[ColIndex[caseSwitch].NameIndex]
, Age = x[ColIndex[caseSwitch].AgeIndex]
, Date = x[ColIndex[caseSwitch].DateIndex]
, Score = x[ColIndex[caseSwitch].ScoreIndex]
};

Related

Validate time against time interval

I have three rules:
A. Time slot between 9am and 3pm
B. Time slot betweem 3pm and 7pm
C. Time slot between 7pm and 9am
Currently I represented them as TimeSpans
public class Rule
{
public string Name {get; set;}
public TimeSpan From {get; set;}
}
List<Rule> rules = new List<Rule>()
{
new Rule() {From = new TimeSpan(9, 0, 0), Name = "A"},
new Rule() {From = new TimeSpan(15, 0, 0), Name = "B"},
new Rule() {From = new TimeSpan(19, 0, 0), Name = "C"}
};
My question is how to validate the time input let's say 9.10pm against that rules?
It should pick third rule.
With a slight modification by adding an end time to the Rule object
public class Rule {
public string Name { get; set; }
public TimeSpan From { get; set; }
public TimeSpan To { get; set; }
}
this extension method was used to check if a provided input is within the time range of the rule.
public static class RuleExtension {
public static bool Contains(this Rule rule, TimeSpan input) {
var value = TimeSpan.Parse(input.ToString());
var start = rule.From;
var end = rule.To;
if (end < start) {
//loopback
end += TimeSpan.FromHours(24);
if (value < start)
value += TimeSpan.FromHours(24);
}
return start.CompareTo(value) <= 0 && value.CompareTo(end) < 0;
}
}
The following unit test was used to validate the extension method and extraxt a rule from a collection. (Note: used FluentAssertions to assert results.)
[TestClass]
public class MyTestClass {
[TestMethod]
public void _ValidateTime() {
var rules = new List<Rule>()
{
new Rule() {From = new TimeSpan(9, 0, 0), To = new TimeSpan(15, 0, 0), Name = "A"},
new Rule() {From = new TimeSpan(15, 0, 0), To = new TimeSpan(19, 0, 0), Name = "B"},
new Rule() {From = new TimeSpan(19, 0, 0), To= new TimeSpan(5, 0, 0), Name = "C"}
};
var input = TimeSpan.Parse("21:10");
rules.FirstOrDefault(r => r.Contains(input))
.Should()
.NotBeNull()
.And
.Match((Rule r) => r.Name == "C");
input = TimeSpan.Parse("08:10");
rules.FirstOrDefault(r => r.Contains(input))
.Should()
.BeNull();
input = TimeSpan.Parse("18:10");
rules.FirstOrDefault(r => r.Contains(input))
.Should()
.NotBeNull()
.And
.Match((Rule r) => r.Name == "B");
input = TimeSpan.Parse("10:10");
rules.FirstOrDefault(r => r.Contains(input))
.Should()
.NotBeNull()
.And
.Match((Rule r) => r.Name == "A");
}
You can use the following:
DateTime input = DateTime.Now;
TimeSpan span = input.TimeOfDay;
for (int i = 0; i < rules.Count - 1; i++) {
if (span >= rules[i].From && span < rules[i + 1].From) {
return rules[i];
}
}
return rules[rules.Count - 1];
Something like below might do the trick:
var currentSpan = DateTime.Now - DateTime.Now.Date;
int ruleIndex = -1;
for (int i = 0; i < rules.Count - 1; i++)
{
if (currentSpan >= rules[i].From && currentSpan < rules[i + 1].From)
{
ruleIndex = i;
break;
}
}
if (ruleIndex == -1 && (currentSpan >= rules.Last().From || currentSpan < rules.First().From))
{
ruleIndex = rules.Count - 1;
}
var rule = rules[ruleIndex];
There are only 24 hours in a day. Could you just make a map array with an enum for assignments:
public enum Category
{
A,
B,
C
}
Then the array. So from your categories above 00:00 - 09:00 would be C.
Category[] values = new Category[24];
for(int i = 0;i<9;i++)
{
values[i] = Category.C;
}
So you could assign each hour similarly.
Now given an hour (say 6am) as an input you can use a switch:
switch(values[6]) // gets the appropriate category.
{
case Category.A:
// handle
break;
case Category.B:
// Handle
break;
case Category.C:
// handle
break;
default:
break;
}

IF statement inside a LINQ SELECT to include columns

Is it possible to include or exclude column within linq Select?
var numberOfYears = Common.Tool.NumberOfYear;
var list = users.Select(item => new
{
Id = item.Id,
Name= item.Name,
City= Item.Address.City.Name,
STATUS = Item.Status,
if(numberOfYears == 1)
{
Y1 = item.Records.Y1,
}
if(numberOfYears == 2)
{
Y1 = item.Records.Y1,
Y2 = item.Records.Y2,
}
if(numberOfYears == 3)
{
Y1 = item.Records.Y1,
Y2 = item.Records.Y2,
Y3 = item.Records.Y3,
}
}).ToList();
}
The idea is that i want to display Y1,Y2,Y3 only if has values
Thanks to the beauty of the dynamic keyword what you need is now possible in C#. Below an example:
public class MyItem
{
public string Name { get; set; }
public int Id { get; set; }
}
static void Main(string[] args)
{
List<MyItem> items = new List<MyItem>
{
new MyItem
{
Name ="A",
Id = 1,
},
new MyItem
{
Name = "B",
Id = 2,
}
};
var dynamicItems = items.Select(x => {
dynamic myValue;
if (x.Id % 2 == 0)
myValue = new { Name = x.Name };
else
myValue = new { Name = x.Name, Id = x.Id };
return myValue;
}).ToList();
}
This will return a list of dynamic objects. One with 1 property and one with 2 properties.
Try this approach:
var numberOfYears = Common.Tool.NumberOfYear;
var list = users.Select(item => new
{
Id = item.Id,
Name = item.Name,
City = Item.Address.City.Name,
STATUS = Item.Status,
Y1 = numberOfYears > 0 ? item.Records.Y1 : 0,
Y2 = numberOfYears > 1 ? item.Records.Y2 : 0,
Y3 = numberOfYears > 2 ? item.Records.Y3 : 0
}).ToList();
Instead of 0, add your default value or null.
Update:
According to your comments, the only option for you is to go dynamic. Here's example with dynamics:
var numberOfYears = 3;
var list = users.Select(x =>
{
dynamic item = new ExpandoObject();
item.Id = x.Id;
item.Name = x.Name;
item.Status = x.Status;
var p = item as IDictionary<string, object>;
var recordsType = x.Records.GetType();
for (var i = 1; i <= numberOfYears; ++i)
p["Y" + i] = recordsType.GetProperty("Y" + i).GetValue(x.Records);
return item;
}).ToList();
You can use the ExpandoObject like this:
var data = providers.Select(provider =>
{
dynamic excelRow = new ExpandoObject();
excelRow.FirstName = provider.FirstName ?? "";
excelRow.MiddleName = provider.MiddleName ?? "";
excelRow.LastName = provider.LastName ?? "";
// Conditionally add columns to the object...
if (someCondition)
{
excelRow.Property1ForCondition = provider.Property1ForCondition;
excelRow.Property2ForCondition = provider.Property2ForCondition;
}
excelRow.DueDate = provider.DueDate ?? null;
.
.
.
return excelRow;
});
Another variation of the above code can be:
var data = new List<ExpandoObject>();
providers.ForEach(provider =>
{
dynamic excelRow = new ExpandoObject();
excelRow.FirstName = provider.FirstName ?? "";
excelRow.MiddleName = provider.MiddleName ?? "";
excelRow.LastName = provider.LastName ?? "";
// Conditionally add columns to the object...
if (someCondition)
{
excelRow.Property1ForCondition = provider.Property1ForCondition;
excelRow.Property2ForCondition = provider.Property2ForCondition;
}
excelRow.DueDate = provider.DueDate ?? null;
.
.
.
data.Add(excelRow);
});

getting a value that is between a from limit and to limit in list

I have a list of Gift. I am passing in a value and I want to check if this value best matches one (or first value out of many) from the list of Gift.
List<GiftConfig> m_Gifts = new List<GiftConfig>() {
new GiftConfig () { Id = 1, From = 5000000, To = 8999999},
new GiftConfig () { Id = 2, From = 9000000, To = 18999999},
new GiftConfig () { Id = 3, From = 1900000, To = 25000000},
new GiftConfig () { Id = 4, From = 35000000, To = 0},
};
Ex: 7000000 => Id = 1
10000000 => Id = 2
This should do the trick
var gifts = m_Gifts.Where(x => value >= x.From && (value <= x.To || x.To == 0));
int value = 7000000;
GiftConfig gift = m_Gifts.FirstOrDefault(x => value >= x.From && value <= x.To);
// Returns Id 1
value = 10000000;
gift = m_Gifts.FirstOrDefault(x => value >= x.From && value <= x.To);
// Returns Id 2
value = 999999999;
gift = m_Gifts.FirstOrDefault(x => value >= x.From && value <= x.To);
// Returns null (assuming GiftConfig is a class).
This will return the first matching gift, or null if there is not one - 'best' is a bit ambiguous. Also a little unsure of what you are expecting to be done with the final record - why is 'To' 0? Is this a special case?

Find the best matching product version from a list of available product versions

How to return the best matching/next available product versionId from a list of available product versions ?
Here is the logic based on the sample data in the table
Look for the best matching version available less than 10.10.20 and should return its versionID
eg1:GetVersion("10.10.20") should return 5 ( because in table there is no "10,10,20" major.minor.build combination available ,so it should look for the best matching version
here the next available version is 10.7.1 ie., versionID 5
eg2:GetVersion("7.0.0") should return 3 ( because in table there is no "7,0,0" major.minor.build combination available ,so it should look for next available matching version .here the
next available version is 6.2.1 ie., versionID 3
eg3:GetVersion("7.5.1") should return 4 ,here exact match is available soit should return versionid 4
[Serializable]
public class ProductVersions
{
[Key]
public int Version_Id { get; set; }
public int Major { get; set; }
public int Minor { get; set; }
public int Build { get; set; }
}
Here is some sample data in my ProductVersions Table
[version_id , Major,Minor,Build]
1 3 0 1
2 4 10 5
3 6 2 1
4 7 5 1
5 10 7 1
6 11 10 10
Here is my method that is expected to return best available product version
private int GetVersion(string versionNumber)
{
int version-id=0;
version-id= //retrieve best matching version
return version-id
}
You can use the build-in Version class, since it already implements the <= operator you are basically looking for, and also can handle the string parsing for you:
var data = new List<Version>()
{
new Version(3,0,1),
new Version(4,10,5),
new Version(6,2,1),
new Version(7,5,1),
new Version(10,7,1),
new Version(11,10,10)
};
var case1 = new Version("10.10.20");
// match1 is 5; the index of a List is 0-based, so we add 1
var match1 = data.FindLastIndex(d => d <= case1) + 1;
var case2 = new Version("7.0.0");
// match2 is 3
var match2 = data.FindLastIndex(d => d <= case2) + 1;
var case3 = new Version("7.5.1");
// match3 is 4
var match3 = data.FindLastIndex(d => d <= case3) + 1;
It should be trivial to convert your sequence of ProductVersions to a list of Version objects.
If you don't want to use the Version class for whatever reason, you can implement the <= (and all other missing operators) yourself:
public class ProductVersions
{
//TODO error checking
public int Version_Id { get; set; }
public int Major { get; set; }
public int Minor { get; set; }
public int Build { get; set; }
public ProductVersions(int major, int minor, int build)
{
Major=major;
Minor=minor;
Build=build;
}
public ProductVersions(string version)
{
var tmp = version.Split('.');
Major = Int32.Parse(tmp[0]);
Minor = Int32.Parse(tmp[1]);
Build = Int32.Parse(tmp[2]);
}
public static bool operator == (ProductVersions a, ProductVersions b)
{
return a.Major==b.Major && a.Minor==b.Minor && a.Build==b.Build;
}
public static bool operator != (ProductVersions a, ProductVersions b)
{
return !(a==b);
}
public static bool operator <= (ProductVersions a, ProductVersions b)
{
if (a == b)
return true;
return a < b;
}
public static bool operator >= (ProductVersions a, ProductVersions b)
{
if (a == b)
return true;
return a > b;
}
public static bool operator < (ProductVersions a, ProductVersions b)
{
if(a.Major==b.Major)
if(a.Minor==b.Minor)
return a.Build < b.Build;
else
return a.Minor < b.Minor;
else
return a.Major < b.Major;
}
public static bool operator > (ProductVersions a, ProductVersions b)
{
if(a.Major==b.Major)
if(a.Minor==b.Minor)
return a.Build > b.Build;
else
return a.Minor > b.Minor;
else
return a.Major > b.Major;
}
And a simple test:
var data = new List<ProductVersions>()
{
new ProductVersions(3,0,1) { Version_Id = 1},
new ProductVersions(4,10,5) { Version_Id = 2},
new ProductVersions(6,2,1) { Version_Id = 3},
new ProductVersions(7,5,1) { Version_Id = 4},
new ProductVersions(10,7,1) { Version_Id = 5},
new ProductVersions(11,10,10) { Version_Id = 6}
};
// ensure data is sorted by version
data.Sort((a,b) => a > b ? 1 : a < b ? -1 : 0);
var case1 = new ProductVersions("10.10.20");
// match1 is 5
var match1 = data.Last(d => d <= case1).Version_Id;
var case2 = new ProductVersions("7.0.0");
// match2 is 3
var match2 = data.Last(d => d <= case2).Version_Id;
var case3 = new ProductVersions("7.5.1");
// match3 is 4
var match3 = data.Last(d => d <= case3).Version_Id;
I like Dominic's answer using the version class (why invent when it exists?) But in case you are wondering here is how to do it without using the Version class and you assume the list is already sorted (so you don't need to sort it like he did).
(TL;DR)
// assume verArray is already ordered (this would need to be sorted otherwise.)
// this where checks for less than or equal to.
int result = verArray.Where(v => (v.Major < major) ||
(v.Major == major && v.Minor < minor) ||
(v.Major == major && v.Minor == minor && v.Build <= build))
.Last().Version_Id;
The full code and test:
public ProductVersions[]verArray = {
new ProductVersions() { Version_Id = 1, Major = 3, Minor = 0, Build = 1 },
new ProductVersions() { Version_Id = 2, Major = 4, Minor = 10, Build = 5 },
new ProductVersions() { Version_Id = 3, Major = 6, Minor = 2, Build = 1 },
new ProductVersions() { Version_Id = 4, Major = 7, Minor = 5, Build = 1 },
new ProductVersions() { Version_Id = 5, Major = 10, Minor = 7, Build = 1 },
new ProductVersions() { Version_Id = 6, Major = 11, Minor = 10, Build = 10 },
};
void Main()
{
string test = "10.10.20";
Console.WriteLine(test + " gives "+GetVersion(test));
test = "7.0.0";
Console.WriteLine(test + " gives "+GetVersion(test));
test = "7.5.1";
Console.WriteLine(test + " gives "+GetVersion(test));
}
private int GetVersion(string versionNumber)
{
string [] input = versionNumber.Split(".".ToCharArray());
int major = int.Parse(input[0]);
int minor = int.Parse(input[1]);
int build = int.Parse(input[2]);
// assume verArray is already ordered (this would need to be sorted otherwise.
int result = verArray.Where(v => (v.Major < major) ||
(v.Major == major && v.Minor < minor) ||
(v.Major == major && v.Minor == minor && v.Build <= build))
.Last().Version_Id;
return result;
}
public class ProductVersions
{
public int Version_Id { get; set; }
public int Major { get; set; }
public int Minor { get; set; }
public int Build { get; set; }
}
This returns the following:
10.10.20 gives 5
7.0.0 gives 3
7.5.1 gives 4
I just checked for the samples you gave. This code works for me.
private int getver(int m, int n, int b)
{
List<ProductVersions> pv = new List<ProductVersions>();
pv.Add(new ProductVersions { Version_Id = 3, Major = 6, Minor = 2, Build = 1 });
pv.Add(new ProductVersions { Version_Id = 4, Major = 7, Minor = 5, Build = 1 });
pv.Add(new ProductVersions { Version_Id = 5, Major = 10, Minor = 7, Build = 1 });
pv.Add(new ProductVersions { Version_Id = 6, Major = 11, Minor = 10, Build = 10 });
int mm = m;
if (m == 0)
mm = int.MaxValue;
int nn = n;
if (n == 0)
nn = int.MaxValue;
int bb = b;
if (b == 0)
bb = int.MaxValue;
var v = pv.FindAll(mj => mj.Major <= m).FindAll(mn => n == 0 ? mn.Major <= mm - 1 && mn.Minor <= nn : mn.Minor <= n).FindAll(bl => b == 0 ? bl.Minor <= nn - 1 && bl.Build <= bb : bl.Build <= b).Last().Version_Id;
return v;
}
I am trying to shrink the list based on the criteria from major, minor and build levels and getting the last entity of the list. Here I assume that the list is sorted based on these values.
I found this one a quick and simple way, but with some limitations.
var v = from p in pv
select new { version = p.Version_Id, val = p.Major * 100000000 + p.Minor * 10000 + p.Build };
int vver=v.ToList().FindAll(pp => pp.val <= m * 100000000 + n * 10000 + b).Last().version;

How to initialize an property value depending on another propertey

I have the following code:
var workOrderList = new List<WorkOrder>(
from index in Enumerable.Range(1, orders.Length)
select new WorkOrder
{
OrderID = orders[index - 1],
Status = status[random.Next(0,status.Length-1)],
TotalQuantity = random.Next(1, 5) * 8,
ScheduleCollection = new ObservableCollection<Schedule>
{
new Schedule
{
Color = colors[random.Next(0,colors.Length-1)],
Model = models[random.Next(0,models.Length-1)],
Status = status[random.Next(0,status.Length-1)],
TotalNumber = To be Updated bases on Total Quantity
}
}
Now I want to update Total Number by either dividing or subtracting value from TotalQuantity .
Use a let clause in your query to extract common expressions:
var workOrderList = new List<WorkOrder>(
from index in Enumerable.Range(1, orders.Length)
let totalQuantity = random.Next(1, 5) * 8
select new WorkOrder
{
OrderID = orders[index - 1],
Status = status[random.Next(0,status.Length-1)],
TotalQuantity = totalQuantity,
ScheduleCollection = new ObservableCollection<Schedule>
{
new Schedule
{
Color = colors[random.Next(0,colors.Length-1)],
Model = models[random.Next(0,models.Length-1)],
Status = status[random.Next(0,status.Length-1)],
TotalNumber = // Do something with totalQuantity
}
}
});

Categories