Make foreach with nested-ifs more compact and readable [closed] - c#

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
Looking for C# syntax to make the following code more elegant, compact and readable. The foreach with the multiple if's take up too much space and look too ugly. I'm thinking LINQ syntax maybe?
(srSys and desSys are strings, and srFl and desFl are ints. The final version will have better variable naming)
public static warMod Gen ( List<AtMap> atMaps ) {
List<AtMap> atMapList = new List<AtMap>();
foreach(var a in atMaps)
{
AtMap atMap = new AtMap();
if (!string.IsNullOrEmpty(a.srSys))
{
atMap.srSys = a.srSys;
}
if (!string.IsNullOrEmpty(a.desSys))
{
atMap.desSys = a.desSys;
}
if (a.srFl != null)
{
atMap.srFl = a.srFl;
}
if (a.desFl != null)
{
atMap.desFl = a.desFl;
}
atMapList.Add(atMap);
}
return new warMod {AtMapArr = atMapList}
}

You could simply put the if's hidden away forever inside of the AtMap class.
public static warMod Gen(List<AtMap> atMaps)
{
List<AtMap> atMapList = new List<AtMap>();
foreach (var a in atMaps)
atMapList.Add(new AtMap(a));
return new warMod { AtMapArr = atMapList };
}
public class AtMap
{
public AtMap(AtMap a)
{
if (!string.IsNullOrEmpty(a.srSys))
srSys = a.srSys;
if (!string.IsNullOrEmpty(a.desSys))
desSys = a.desSys;
if (a.srFl != null)
srFl = a.srFl;
if (a.desFl != null)
desFl = a.desFl;
}
}

EDIT: Given that the overall logic of the function seems to just take an List<AtMap> type named AtMaps and make another List<AtMap> type named AtMapsList>. The function can simply be return new warMod {AtMapArr = atMaps};. However, the constructor can still be used at the part where AtMaps is made
// Use of constructor to initialize your class (AtMap.cs)
public class AtMap {
public string srSys, desSys;
public int? srFl, desFl;
public AtMap(string srSys, string desSys, int? srFl, int? desFl) {
if (!string.IsNullOrEmpty(a.srSys)) {
this.srSys = a.srSys;
}
if (!string.IsNullOrEmpty(a.desSys)) {
this.desSys = a.desSys;
}
if (a.srFl != null) {
this.srFl = a.srFl;
}
if (a.desFl != null) {
this.desFl = a.desFl;
}
}
}

When you initialize your class AtMap fields: srSys and desSys will be empty, srFl and desFl (for example: int) will be 0.
I think it can be like this:
public class AtMap
{
public string srSys {get; set;}
public string desSys {get; set;}
public int srFl {get; set;}
public int desFl {get; set;}
}
public static warMod Gen(List<AtMap> atMaps)
{
var atMapList = new List<AtMap>();
foreach (var a in atMaps)
{
var atMap = new AtMap
{
srSys = a.srSys,
desSys = a.desSys,
srFl = a.srFl ?? default(int),
desFl = a.desFl ?? default(int)
};
atMapList.Add(atMap);
}
return new warMod { AtMapArr = atMapList };
}

Related

Array of Objects to String - ASP.NET C# [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 months ago.
Improve this question
Trying to convert Array of Objects to String using C#. Able to achieve the same using LINQ, however trying to make use of reusable functions which would accept array of Objects and return back string. Understand that generics must be used but it's been hard time understanding it. Thanks in advance!
public class HelloWorld {
public static void Main() {
Root root = new Root();
List<A> obj = new List<A>();
obj.Add(new A() { Code = "WAY"});
obj.Add(new A() { Code = "DOWN"});
obj.Add(new A() { Code = "WE"});
obj.Add(new A() { Code = "GO"});
root.A = obj;
string _Result = string.Join("-", root.A.Where(x => x.Code != "").Select(p => p.Code.ToString()).ToArray());
Console.WriteLine(_Result); //Expected OP: WAY-DOWN-WE-GO
Console.WriteLine(Utility.ToArray(root.A)); //System.Collections.Generic.List`1[A]
}
//Trying for much simpler Generic function here.
public class Utility{
public static string ToArray(IList<Object> obj){
foreach(var v in obj){
//generic function..
}
StringBuilder sb = new StringBuilder();
return sb.ToString();
}
}
}
public class Root
{
public List<A> A { get; set; }
public List<B> B { get; set; }
}
public class A
{
public string Code { get; set; }
}
public class B
{
public string Mode { get; set; }
}
If you're looking for a system that works without having to have your classes implement an interface (for objects you didn't create, for example), it's possible to use a Func<T, object> to select a specific property/field:
// IEnumerable is more generic over "lists" (sets, maps, etc)
// \/
public static string ToArray<T>(IEnumerable<T> obj, Func<T, object> func) {
return string.Join('-', obj.Select(func));
}
Usage example:
List<A> aObjects = new();
//add aObjects
List<B> bObjects = new();
//add bObjects
Console.WriteLine(Utilities.ToArray(aObjects, a => a.Code));
Console.WriteLine(Utilities.ToArray(bObjects, b => b.Mode));
Reference: How to join as a string a property of a class?

Problem while updating a child list in C # ASP.Net MVC

public class PayRateDaysModel
{
public string day_name { get; set; }
public List<RateList> multiplier { get; set; }
}
public class RateList
{
public double start_after { get; set; }
public double rate_multiplier { get; set; }
}
daysModel is a List of type PayRateDaysModel. When I'm trying to update multiplier in current object of daysModel list i.e. dayExists, then its updating multiplier in all the elements of daysModel list. I want to update only in current item.
Below is my code :
var dayExists = daysModel.Where(x => x.day_name == day_name).FirstOrDefault();
if(dayExists==null)
{
PayRateDaysModel days = new PayRateDaysModel();
days.day_name = day_name;
days.multiplier = rate_list;
daysModel.Add(days);
}
else
{
//update
dayExists.day_name = "abc";
dayExists.multiplier.FirstOrDefault().rate_multiplier = 1;
}
Based on what you have shown to us I would think that you are creating the rate_list somewhere above like rate_list = new RateList(…) and you are setting this to all of your days in days.multiplier = rate_list;. Since you did not recreate that rate_list for every element, any time you change it in one of your dayExists you will change it for all the others as well. So you should do something like this days.multiplier = new RateList(…);
Check this link : Updating child items in List updates all Items in C#
I don't see any reason for it not to work. Are you sure you are testing it in right way?
I think it will be better to improve the code and test for null value :
var dayExists = daysModel.Where(x => x.day_name == day_name).FirstOrDefault();
if(dayExists==null)
{
PayRateDaysModel days = new PayRateDaysModel();
days.day_name = day_name;
days.multiplier = rate_list;
daysModel.Add(days);
}
else
{
//update
dayExists.day_name = "abc";
var firstMultiplier= dayExists.multiplier.FirstOrDefault();
if( firstMultiplier!=null)
{
firstMultiplier.rate_multiplier = 1;
}
}

Best way to convert list data to another with a desired format in c# using LINQ [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
How I need to insert a chart in my project and I use the C3 library to do so, which expect a specific format to work.
I retrieve my data from my database and I get it into this format
{"TotalbeAbsencesDepartmentByReason":[
[ {"Label":"DQUA","Reason":"AUT","AbsNumber":1,"AbsenceHours":3.75},
{"Label":"DQUA","Reason":"NOAUT","AbsNumber":1,"AbsenceHours":3.75},
{"Label":"DQUA","Reason":"CM","AbsNumber":4,"AbsenceHours":32}
],
[{"Label":"DPRO","Reason":"AUT","AbsNumber":10,"AbsenceHours":43.9},{"Label":"DPRO","Reason":"CM","AbsNumber":18,"AbsenceHours":144}],
[{"Label":"DMAI","Reason":"AUT","AbsNumber":2,"AbsenceHours":1.6999999999999993}],
[{"Label":"DENG","Reason":"CM","AbsNumber":2,"AbsenceHours":16}]
]}
My expected format that I want to work on is :
"TotaleAbsDepartementByReason":
[
{"Label":"DQUA","Aut":3.75,"NoAut":3.75,"CM":32},
{"Label":"DPRO","Aut":43.9,"NoAut":0,"CM":144},
{"Label":"DMAI","Aut":1.6999999999999993,"NoAut":0,"CM":0},
{"Label":"DENG","Aut":0,"NoAut":0,"CM":16}
]
This is my solution it works perfectly, But I think that there is another way to proceed for better performance :
var DeptNames = TotalbeAbsencesDepartmentByReason.Select(b => b.Label).Distinct();
var TotaleAbsDepartementByReason = new List<AbsByReason>();
foreach (var name in DeptNames)
{
var auth = TotalbeAbsencesDepartmentByReason.Where(a => a.Label == name && a.Reason == "AUT").FirstOrDefault();
var noauth = TotalbeAbsencesDepartmentByReason.Where(a => a.Label == name && a.Reason == "NOAUT").FirstOrDefault();
var cm = TotalbeAbsencesDepartmentByReason.Where(a => a.Label == name && a.Reason == "CM").FirstOrDefault();
TotaleAbsDepartementByReason.Add(new AbsByReason
{
Aut = auth != null ? auth.AbsenceHours.Value : 0 ,
CM = cm != null ? cm.AbsenceHours.Value : 0,
NoAut = noauth != null ? noauth.AbsenceHours.Value : 0,
Label = name
});
}
You can organize your code slightly better, at-least from maintainability perspective, and removing some redundancies should hopefully offer some performance boost. Below is the refactored code
public class RowData {
public string Label { get; set; }
public string Reason { get; set; }
public int AbsNumber { get; set; }
public decimal? AbsenceHours { get; set; }
}
public class AbsByReason {
public decimal Aut { get; set; }
public decimal CM { get; set; }
public string Label { get; set; }
public object NoAut { get; set; }
}
public static IEnumerable<AbsByReason> TransformData(List<RowData> totalbeAbsencesDepartmentByReason) {
return totalbeAbsencesDepartmentByReason
.GroupBy(row => row.Label)
.Select(XFormLabelGroupToAbsByReason);
}
private static AbsByReason XFormLabelGroupToAbsByReason(IEnumerable<RowData> labelGroup) {
var reason = new AbsByReason();
foreach (var rowData in labelGroup) {
if (rowData.Reason == "AUT")
reason.Aut = rowData.AbsenceHours ?? 0;
else if (rowData.Reason == "NOAUT")
reason.NoAut = rowData.AbsenceHours ?? 0;
else if (rowData.Reason == "CM")
reason.CM = rowData.AbsenceHours ?? 0;
}
return reason;
}
Let me explain what I have done. GroupBy is very helpful when using grouped data as you are in this case. Once you group your data, you only need to transform that group (instead of all the data in the list). You can use LINQ, but I would rather use a simple for-loop and if-else-if combination. I find it better to do things in single-pass than in multi-pass LINQ way even though you are pretty much talking about no more than 3 items in the list.
One problem I see is that you traverse the original list three times with different filter predicates. Here's a way how you can get the same result with only one list traversal:
var Depts = new Dictionary<string, AbsByReason>();
foreach (var entry in TotalbeAbsencesDepartmentByReason)
{
AbsByReason abs;
if (!Depts.TryGetValue(entry.Label, out abs))
Depts[entry.Label] = abs = new AbsByReason() { Label = entry.Label };
switch (entry.Reason)
{
case "AUT":
abs.Aut = entry.AbsenceHours.Value;
break;
case "NOAUT":
abs.NoAut = entry.AbsenceHours.Value;
break;
case "CM":
abs.CM = entry.AbsenceHours.Value;
break;
}
}
var TotaleAbsDepartementByReason = Depts.Select(kvp => kvp.Value).ToList();

Permanently in while loop c# , Visual Studio 2013

with my code, I'm supposed to be able to call a question from my file at random and not reuse that question again.
But for some reason I'm stuck in the while loop and after a few hours of searching I still can't see why, so I hope you guys can help me out.
(he gets stuck after he generated 8 questions)
Code (for the generatequestion one, if you need more I can paste it but I hope this gives enough information):
lineCount = File.ReadLines(pad).Count();
questions = new string[lineCount, 2];
public string GenerateQuestion(int total)
{
if (total <= 10)
{
Random ran = new Random();
questionNumber = ran.Next(lineCount);
while (previousQuestions.Contains(questionNumber))
{
questionNumber = ran.Next(lineCount);
}
}
previousQuestions[questionCount] = questionNumber;
questionCount++;
return questions[questionNumber, 0];
}
You could make a method to return the lines in the file in random order.
public string[] GetQuestionsInRandomOrder()
{
var lines = File.ReadAllLines("test.txt");
var rnd = new Random();
lines = lines.OrderBy(line => rnd.Next()).ToArray();
return lines;
}
Then as you use the questions you can remove them from the array.
var isRemoved = Array.remove(array, item);
https://msdn.microsoft.com/en-us/library/vstudio/bb397721%28v=vs.100%29.aspx
This is easier that what I had before and will give greater control. I would create a class for this and this example is a starting point. You can add more functionality as you go along. By having a class to do all this work you can add methods to do additional features later on without having to rewrite code.
public class Logic
{
public List<string> AllQuestionsInRandomOrder { get; set; }
public List<string> QuestionsThatHaveBeenRemoved { get; set; }
public Logic()
{
QuestionsThatHaveBeenRemoved = new List<string>();
AllQuestionsInRandomOrder = GetQuestionsInRandomOrder().ToList();
}
public string GetUnusedQuestion()
{
var question =
AllQuestionsInRandomOrder.FirstOrDefault(x => !QuestionsThatHaveBeenRemoved.Contains(x));
QuestionsThatHaveBeenRemoved.Add(question);
return question;
}
public IEnumerable<string> GetQuestionsInRandomOrder()
{
var lines = File.ReadAllLines("test.txt").ToList();
var rnd = new Random();
lines = lines.OrderBy(line => rnd.Next()).ToList();
return lines;
}
public void RemoveQuestion(List<string> questions, string questionToRemove)
{
questions.Remove(questionToRemove);
}
}
I would avoid using loops and break the problem into separate methods or functions. It will make it easier to debug. This implementation does not separate the questions and answers out, so you will need to add a method that returns a dictionary of the questions and answers.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var questions = new Questions();
var firstQuestion = questions.GetUnusedQuestion();
Console.WriteLine(firstQuestion);
}
}
class Questions
{
public string FileName { get; set; }
public static List<string> AllQuestionsAndAnswersFromFile { get; set; }
public List<string> AllQuestionsInRandomOrder { get; set; }
public List<string> QuestionsThatHaveBeenRemoved { get; set; }
public Questions()
{
FileName = "text.txt";
QuestionsThatHaveBeenRemoved = new List<string>();
AllQuestionsAndAnswersFromFile = new List<string>();
ReadQuestionsFromFile();
AllQuestionsInRandomOrder = GetQuestionsInRandomOrder().ToList();
}
public string GetUnusedQuestion()
{
var question =
AllQuestionsInRandomOrder.FirstOrDefault(x => QuestionsThatHaveBeenRemoved.Contains(x));
QuestionsThatHaveBeenRemoved.Add(question);
return question;
}
private static IEnumerable<string> GetQuestionsInRandomOrder()
{
var lines = AllQuestionsAndAnswersFromFile;
var rnd = new Random();
lines = lines.OrderBy(line => rnd.Next()).ToList();
return lines;
}
public void RemoveQuestion(List<string> questions, string questionToRemove)
{
questions.Remove(questionToRemove);
}
public void ReadQuestionsFromFile()
{
using (var reader = new StreamReader(FileName, Encoding.Default))
{
var text = reader.ReadToEnd();
var lines = text.Split('=');
foreach (var s in lines)
{
AllQuestionsAndAnswersFromFile.Add(s);
}
}
}
}
}
When you run out of questions, let's see what
while (previousQuestions.Contains(questionNumber))
{
questionNumber = ran.Next(lineCount);
}
actually does:
Is questionNumber one of the questions we already asked?
Yes, get a new question number. (because we've asked all the questions)
Is questionNumber one of the questions we already asked?
yes, get a new question number. (because we've asked all the questions)
A solution in this case would be to shuffle your questions that you return, removing them as you grab them.

C# help in classes [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have two classes, SuperHero and SuperTeam. How can I add instances of SuperHero to the TeamList property of SuperTeam?
namespace SuperLeague
{
class SuperHero
{
string SuperHeroName;
string ComicTitle;
public SuperHero()
{
SuperHeroName = "";
ComicTitle = "";
}
public SuperHero(string nSuperHeroName,string nComicTitle)
{
SuperHeroName = nSuperHeroName;
ComicTitle = nComicTitle;
}
public string nSuperHeroName
{
get { return SuperHeroName; }
set { SuperHeroName = nSuperHeroName; }
}
public string nComicTitle
{
get { return ComicTitle; }
set { ComicTitle = nComicTitle; }
}
}
class SuperTeam
{
string SuperTeamName;
List<SuperTeam> TeamList = new List<SuperTeam>();
public SuperTeam()
{
SuperTeamName = "";
}
public SuperTeam(string nSuperTeamName)
{
SuperTeamName = nSuperTeamName;
}
public string nSuperTeamName
{
get { return SuperTeamName; }
set { SuperTeamName = nSuperTeamName; }
}
public void SuperTeamAdd(SuperHero NewHero)
{
TeamList.Add(NewHero);
}
public void SuperTeamRemove(string NameToFind)
{
SuperHero SuperHeroToDel = null;
for (int i = 0; i < TeamList.Count; i++)
{
if (TeamList[i].nSuperHeroName.Equals(NameToFind))
{
SuperHeroToDel = TeamList[i];
TeamList.Remove(SuperHeroToDel);
}
}
}
}
}
Given the provided code;
Your SuperHero class represents a single hero.
Your SuperTeam class represents a single team which contains a list of hero's.
If you want multiple team's with super hero's we'll need another container. Something like
List<SuperTeam> listOfTeams;
This will allow you to store multiple "SuperHero"'s into the "SuperTeam", and store multiple teams in "listOfTeams"
Since you mention you want to get a team used on it's name, you could also use a "Dictionary<string, SuperTeam> CollectionOfTeams". This will allow you to get a team from 'CollectionOfTeams' using the a key.
SuperHero superMan = new SuperHero();
// fill in superMan properties
SuperTeam flyingTeam = new SuperTeam();
flyingTeam.members.Add(superMan);
// add more super hero members that fly to the team
collectionOfTeams.Add("Flying", flyingTeam);
// Getting the 'flying team'
SuperTeam currentTeam = CollectionOfTeams["Flying"];
Hope this helps,
Like this:
SuperHero hero = new SuperHero();
SuperTeam team = new SuperTeam();
team.TeamList.Add(hero);
Also, change the TeamList property to this:
List<SuperHero> TeamList;

Categories

Resources