Hi there I am making a C# windows forms app, I made a switch with all my teammates birthdays in order to calculate their birthday age, however I want to add the functionality that if today is their birthday a message appears.
Therefore I need something that goes into EACH CASE of the switch and there I will add a validation like if today's date is equal to bday, then messageshow "its name 1 birthday, today!"
switch (name)
{
case "name 1":
bday = DateTime.Parse("11.11.1988");
break;
case "name 2":
bday = DateTime.Parse("11.12.1988");
break;
case "name 3":
bday = DateTime.Parse("03.12.1987");
break;
}
Why dont you use a Dictionary.
Dictionaries use other objects as Keys to retrieve their values.
In your case you could map all the birthdays to their names like:
Dictionary<string, DateTime> birthdays = new Dictionary<string, DateTime>;
//Add values like this
birthdays.Add("name 1", DateTime.Parse("11.11.1988"));
birthdays.Add("name 2", DateTime.Parse("11.12.1988"));
...
//Then you could loop through all the entries
foreach(KeyValuePair<string, DateTime> entry in birthdays)
{
if(entry.Value.Day == DateTime.Now.Day && entry.Value.Month == DateTime.Now.Month)
{
Console.WriteLine(entry.Key + " has birthday!");
}
}
Based on what you provided in your snippet, you could do this sort of check outside of the case statement. Example:
public void WhateverYourMethodIs()
{
switch (name)
{
case "name 1":
bday = DateTime.Parse("11.11.1988");
break;
case "name 2":
bday = DateTime.Parse("11.12.1988");
break;
case "name 3":
bday = DateTime.Parse("03.12.1987");
break;
}
if (this.IsBirthday(bday))
{
// do whatever you want for when it's the name's birthday.
}
}
public bool IsBirthday(DateTime bday)
{
if (bday.Day == DateTime.Now.Day && bday.Month == DateTime.Now.Month)
return true;
return false;
}
note the above would not take into account leap day birthdays.
Based on your comments, it sounds like you're hoping to evaluate the birthdays of all names regardless of the switch. This won't work in your current approach. bday is a single value that can only be attributed to the current "name".
One way to accomplish what you're hoping for would be to use a class to represent a name as so:
public class User
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
public bool IsBirthday
{
get
{
if (Birthday.Day == DateTime.Now.Day && Birthday.Month == DateTime.Now.Month)
return true;
return false;
}
}
}
public class SomeClass
{
private List<User> Users = new List<User>();
public void PopulateNames()
{
Users.Add(new User()
{
Name = "name 1",
Birthday = DateTime.Parse("11.11.1988")
};
Users.Add(new User()
{
Name = "name 2",
Birthday = DateTime.Parse("11.12.1988")
};
// etc
}
public void DoSomethingOnUsersWithABirthday()
{
foreach (User user in Users)
{
if (user.IsBirthday)
{
// do something for their birthday.
Console.WriteLine(string.format("It's {0}'s birthday!", user.Name));
}
}
}
}
Related
I am wondering if there is some clever way to retrieve data from an enumerable using LINQ when individual values from multiple records are needed.
For example, let's say you have a person with three different phone fields:
public class Person
{
public Phone HomePhone { get; set; }
public Phone WorkPhone { get; set; }
public Phone CellPhone { get; set; }
}
...but the phone list is stored in a normalized format:
public enum PhoneType
{
Home, Work, Cell
}
public class Phone
{
public PhoneType Type { get; set; }
public string Number { get; set; }
}
static public IEnumerable<Phone> GetPhoneList()
{
yield return new Phone { Type = PhoneType.Home, Number = "8005551212" };
yield return new Phone { Type = PhoneType.Work, Number = "8005551313" };
yield return new Phone { Type = PhoneType.Cell, Number = "8005551414" };
}
If you needed to populate Person, you could write a loop, and get everything you need in one pass:
public static Person GetPerson1()
{
var result = new Person();
foreach (var ph in GetPhoneList())
{
switch (ph.Type)
{
case PhoneType.Home: result.HomePhone = ph; break;
case PhoneType.Work: result.WorkPhone = ph; break;
case PhoneType.Cell: result.CellPhone = ph; break;
}
}
return result;
}
But if you wanted to use LINQ, it seems like three passes may be needed:
public static Person GetPerson2()
{
return new Person
{
HomePhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Home ),
WorkPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Work ),
CellPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Cell )
};
}
Is there a clever way to use LINQ to get it all with only one pass over the enumeration?
Here is a link to a Fiddle if you'd like to explore my code.
(I am aware I could use a dictionary or other data structure to solve this particular problem; this is just an example.)
Normally, you can't do this in LINQ.
If you really want to, you can create a Foreach extension method and do the same as your GetPerson1 method.
public static class Ext
{
public static void Foreach<T>(this IEnumerable<T> e, Action<T> action)
{
foreach (T item in e)
{
action(item);
}
}
}
and then
public static Person GetPerson2()
{
var p = new Person();
var pl = GetPhoneList();
pl.Foreach(ph =>
{
switch (ph.Type)
{
case PhoneType.Home: p.HomePhone = ph; break;
case PhoneType.Work: p.WorkPhone = ph; break;
case PhoneType.Cell: p.CellPhone = ph; break;
}
});
return p;
}
But you really shouldn't. LINQ is meant to operate on IEnumerables (item by item), and LINQ functions should be without side effects, while your foreach loop and Foreach extension methods are only creating side effects, changing the state of the Person object.
And, besides, the fact that you need a 'clever way' should be an indication that this is not the way it's meant to be used :)
There's a great article by Eric Lippert with more details here: https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
If there is no guarantee that numbers from the same person come in a sequence then you have to enumerate the list until you find all the numbers. It does not seem to me this is a good candidate for LINQ, whose purpose is to make the code more readable. Your foreach is just fine, and I would just break the loop when all numbers are found.
If you want to enumerate all the persons, and not just one then Dictionary approach is probably most effective. GroupBy internally uses a dictionary and you can use GroupBy to collect all the numbers belonging to a person, and then Aggregate to make a Person out of them. Let's assume there is some property Phone.PersonID, and also Person.PersonID, then you would have something like this:
GetPhoneList()
.GroupBy(x => x.PersonID)
.Select(x => x.Aggregate(new Person() { PersonID = x.Key },
(person, phone) =>
{
switch (phone.Type)
{
case PhoneType.Home: person.HomePhone = phone; break;
case PhoneType.Work: person.WorkPhone = phone; break;
case PhoneType.Cell: person.CellPhone = phone; break;
}
return person;
}));
I assume here that GetPhoneList() returns all the phones of all persons.
Hi I am trying to build a contact managers program using an list to store and display the data. I need to view a report that displays a summary of contacts available and then have a menu to allow the user to interact with the program. I need to make a method to allow the user to create a new contact which will contain their first and last name, phone number, email address and type. but I am not sure as to how to do this with an array.
Any guidance would be appreciated.
static void Main(string[] args)
{
//The MAXPLAYERS constant is the physical table size
const Int32 MAXCONTACTS = 23;
//Declare the player tables
Contact[] contacts = new Contact[MAXCONTACTS];
//Keep track of the actual number of contacts (i.e. logical table size)
Int32 contactCount = 0;
Int32 number = 0;
String lastName = " ";
String phoneNumber = " ";
String emailAddress = " ";
String firstName = " "; ;
Console.WriteLine("Contact List");
// display the menu to the user
Console.WriteLine("Enter option or M for menu:");
//Main Driver
char menuItem;
menuItem = GetMenuItem();
while (menuItem != 'X')
{
ProcessMenuItem(menuItem, firstName, lastName, phoneNumber, emailAddress, contacts, ref contactCount, MAXCONTACTS);
menuItem = GetMenuItem();
}
Console.WriteLine("\nThank you, goodbye");
Console.ReadLine();
}
static char GetMenuItem()
{
char menuItem;
DisplayMenu();
menuItem = IOConsole.GetChar((Console.ReadLine()));
while (menuItem != 'C'
&& menuItem != 'L' && menuItem != 'X' && menuItem != 'R' && menuItem != 'U' && menuItem != 'D')
{
Console.WriteLine("\nError - Invalid menu item");
DisplayMenu();
//menuItem = IOConsole.GetChar((Console.ReadLine()));
}
return menuItem;
}
static void DisplayMenu()
{
Console.WriteLine("C-> Create Contacts");
Console.WriteLine("R-> Remove Contacts");
Console.WriteLine("U-> Update Contacts");
Console.WriteLine("D -> Load data from file");
Console.WriteLine("S-> Save data to file");
Console.WriteLine("L-> View sorted by last name");
Console.WriteLine("F-> View sorted by first name");
Console.WriteLine("P-> View by partial name search");
Console.WriteLine("T-> View by contact type");
Console.WriteLine("Q-> Quit");
}
//Routes to the appropriate process routine based on the user menu choice
static void ProcessMenuItem(Char menuItem, String firstName, String lastName, String phoneNumber,
String emailAddress, Contact[] contacts, ref Int32 contactCount, Int32 MAXCONTACTS)
{
switch (menuItem)
{
case 'C':
createContact();
break;
case 'R':
removeContact();
break;
case 'U':
updateContact();
break;
case 'D':
LoadToFile();
break;
case 'S':
saveToFile();
break;
case 'L':
sortByLastName();
break;
case 'F':
sortByFirstName();
break;
case 'P':
break;
case 'T':
break;
case 'Q':
break;
}
}
public static void createContact()
{
}
Here is the enumeration method that is inside my contact object class and is used for the type.
enum ContactTypesEnum { Family, Friend, Professional }
There are many possible ways to achieve this and one could be declaring an object of type List with generic type on it such as List<T> where T is the type of an object.
You can declare either a List<Contact> or derive a class based on List<Contact>.
For further learning I would suggest to check some articles for List<T> class.
Reference: https://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx
And you can also check the code below as additional learning material:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
namespace TestContacts
{
public enum ContactTypesEnum {
Family,
Friend,
Professional
}
public class Contact
{
public int Number { get; set; }
public string PhoneNumber { get; set; }
public string EmailAddress { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ContactTypesEnum Type { get; set; }
}
//I would suggest you check the List collection with generic data types
//so you will be able to assign your own objects
public class ContactCollection : List<Contact>
{
public Int32 ContactCount
{
get
{
return this.Count;
}
}
}
class Program
{
static void Main(string[] args)
{
Contact c1 = new Contact();
c1.Number = 1; //Id?
c1.LastName = "Doe";
c1.FirstName = "John";
c1.EmailAddress = "johndoe#email.com";
c1.PhoneNumber = "12345678";
c1.Type = ContactTypesEnum.Friend;
//Create more contacts...
//Add all contacts here
ContactCollection contactList = new ContactCollection();
contactList.Add(c1);
//Loop through list
foreach( Contact c in contactList)
{
Console.WriteLine(c.FirstName); //Do something with fields
}
Console.ReadLine();
}
}
}
If you use the List<T> instead of Array, as Joel Legaspi Enriquez said, you can easily use lots of useful Linq functions on a list as an Enumerable object by adding using System.Linq; at the begging of your program.
I'm sure you can use it by your own. or Google Linq in C#! ;)
I have my enum like this.
public enum Gender
{
Man = 1,
Woman = 2
}
And I use ASP MVC4 to display the choices in a drop down like this.
#Html.DropDownListFor(model => model.Gender, new SelectList(Enum.GetValues(typeof(Namespace.Models.Enum.Gender))))
This works like a charm, it display Man/Woman in the drop down.
My problem is that I would like to show different names on those enums in different contexts.
Like one context would be if you are a Mom or a Dad. I would like to use the gender enum as base, but display Mom/Dad instad of Man/Woman.
Another context would be Boy/Girl, I still would like to use the gender enum, but display a different text.
Is this possible in any way?
EDIT
I used Kevin's solution and also added another extention method like this.
public static List<KeyValuePair<string, int>> GetValues(IGenderStrategy genderStrategy)
{
Dictionary<string, int> arr = new Dictionary<string, int>();
foreach (Gender g in System.Enum.GetValues(typeof(Gender)))
arr.Add(g.ToValue(genderStrategy), (int)g);
return arr.ToList();
}
Which I used like this in my view.
#Html.DropDownListFor(model => model.Gender, new SelectList(Chores.Models.Enum.EnumExtentions.GetValues(new Chores.Models.Enum.ParentStrategy()), "value", "key"))
I like #RakotVT answer of using an extension method but would extend it a bit further as you would need a new extension method for every situation which is not great.
I think a variation of the Strategy pattern might work better here (http://www.dofactory.com/Patterns/PatternStrategy.aspx)
Something like this -
public enum Gender
{
Man = 1,
Woman = 2
}
public interface IGenderStrategy
{
string DisplayName(Gender gender);
}
public class ParentStrategy : IGenderStrategy
{
public string DisplayName(Gender gender)
{
string retVal = String.Empty;
switch (gender)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
default:
throw new Exception("Gender not found");
}
return retVal;
}
}
public static class EnumExtentions
{
public static string ToValue(this Gender e, IGenderStrategy genderStategy)
{
return genderStategy.DisplayName(e);
}
}
public class Test
{
public Test()
{
Gender.Man.ToValue(new ParentStrategy());
}
}
Try to add Extentions class for your Enum. Here is an example of this class.
public static class EnumExtentions
{
public static string ToChildValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Boy";
break;
case Gender.Woman:
retVal = "Girl";
break;
}
return retVal;
}
public static string ToParentValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
}
return retVal;
}
}
Dunno if this is the neatest way, but how about something like:
#functions{
IEnumerable<SelectListItem> GetGenderSelectList(GenderContext genderContext)
{
return Enum.GetValues(typeof(Namespace.Models.Enum.Gender)).ToList().ConvertAll(x => new SelectListItem(){Value= x.ToString(), Text= GetGenderDescription(x, genderContext)});
}
string GetGenderDescription(Gender gender, GenderContext genderContext)
{
switch (GenderContext)
{
case Children: return gender == Man? "Boy" : "Girl";
case Parents: return gender == Man? "Dad" : "Mom";
default: return gender.ToString();
}
}
}
#Html.DropDownListFor(model => model.Gender, GetGenderSelectList(model.GenderContext))
Here 'GenderContext' is another Enum.
obviously you don't need to have those functions in the page functions - Could just add the list of items to the ViewBag before even getting to the view.
To give some background I'm trying to solve the Project Euler Problem 54 involving poker hands. Though there's infinite approaches to this. What I would like to do is enumerate through a list of strings, for example:
{ "8C", "TS", "KC", "9H", "4S" };
I would like to "get" an instance of class card with properties value, and suit, for each respective string. I've not yet utilized get/set so maybe there is an obvious approach to this I'm missing.
Ultimately I would like to have a list of objects type Card, I don't mind building all the card's ahead of time, such that "2H" returns an instance of type Card where suit = Hearts, and value = 2, for example.
I know this code is wrong, but it should give an idea of what I'm trying to do. Any suggestions would be appreciated.
class Card
{
public string suit;
public int value;
public string cardname
{
get
{
if (cardname == "2H") Card TwoH = new Card();
TwoH.suit = "Hearts"
TwoH.value = 2;
return TwoH;
}
}
}
Why not make a constructor that fills suit and value based on a string parameter
public Card(string name)
{
switch(name)
{
case "2H":
this.suit = "Hearts";
this.value = 2;
break;
//...
}
}
This might not be the exact solution you seem to be asking for but if the values you'll be getting (eg 2H, 3C etc) are all 2 characters long, then you can try this:
public class Card
{
public string suit { get; set; }
public int value { get; set; }
public static Card GetCard(string cardName)
{
string tmpSuit;
int tmpValue;
char[] cardNameParts = cardName.ToCharArray();
switch(charNameParts[0])
{
case "A":
tmpValue = 1;
break;
case "2":
tmpValue = 2;
break;
...
}
switch(charNameParts[1])
{
case "H":
tmpSuit= "Hearts";
break;
case "C":
tmpSuit= "Clubs";
break;
...
}
return new Card() { suit = tmpSuit, value = tmpValue };
}
}
I would do it like that:
public class Card
{
public string Suit { get; set; }
public int Value { get; set; }
public static Card FromString(string s)
{
if (s == "2H") return new Card() { Suit = "Hearts", Value = 2 };
else if (s == "....")
...
else return null;
}
}
I have converted your suit and value field into properties and instead of some getter method which in your case wouldn't work I have added a static method.
You can use it like this Card card2H = Card.FromString("2H");
Maybe use two switch statements, first
switch (cardname[0])
{
...
}
then
switch (cardname[1])
{
...
}
Before that, check that cardname.Length == 2. In each switch, have a default section where you throw an exception in case the char value doesn't make sense.
I have a requirement to verify whether the postal code for a particular country is mandatory or not based on the countryid supplied. Currently I'm doing this with a switch statement but this code breaks the Open/Closed SOLID principle. I would like to know how to get rid of the switch in this scenario.
public class PostCodeVerifyMandatory : IPostCodeVerifyMandatory {
public bool IsPostCodeRequired(int countryId, string region)
{
switch (countryId) {
case 1: //UK
case 12: //Australia
case 29: //Brazil
case 31: //Brunei
case 37: //Canada
case 56: //Denmark
case 105: //Japan
case 110: //South Korea
case 114: //Latvia
case 136: //Moldova
case 137: //Monaco
case 145: //Netherlands
case 165: //Poland
case 166: //Portugal
case 183: //Slovak Republic (Slovakia)
case 189: //Spain
case 196: //Sweden
case 197: //Switzerland
case 199: //Taiwan Region
case 213: //Ukraine
case 215: //USA
case 221: //Vietnam
return true;
case 232: //Ireland
return region == "Dublin";
default:
return false;
}
}
}
Your switch statement effectively maps integers to booleans, with a default value of false.
So in this case, I would simply create a Dictionary<int,bool> with the appropriate values. Since the values are pretty much fixed, you could initialize it in the declaration:
Dictionary<int, bool> dict = new Dictionary<int, bool>() {
{ 1 /* UK */, true },
{ 12 /* Australia */, false }
...etc...
};
As #Nick points out, the case for Ireland means you'll still need some extra logic, so you'll want the Dictionary to be private, and the answer accessible via your IsPostCodeRequired(int,strnig) method.
EDIT:
It would probably be best to get these values from a database, as #JustinHarvey points out.
If you want to be very strict about the Open/Closed Principle, you could use the Strategy design pattern - you would create a separate ConcreteStrategy object for every country. If a new country were added, you would create a new ConcreteStrategy object for that country. That way you can add the logic for countries with special rules, without touching your original code.
However, the number of countries with special rules is probably very small, so unless you really cannot change the code once it's in production, this is over-engineering.
Maybe something like this:
private Dictionary<int, string> _dict;
protected Dictionary<int, string> CountryDictionary
{
get
{
if (_dict == null)
{
_dict = new Dictionary<int, string>();
_dict.Add(1, "UK");
_dict.Add(12, "Australia");
// and so on
}
return _dict;
}
}
public class PostCodeVerifyMandatory : IPostCodeVerifyMandatory
{
public bool IsPostCodeRequired(int countryId, string region)
{
return CountryDictionary.ContainsKey(countryId);
}
}
I'd probably follow that piece of advice from the c2 Wiki page "Switch statements smell" :
Using a database or TableOrientedProgramming is sometimes the
appropriate "fix", not polymorphism. For example, store product
classications are best handled in a database with many-to-many
category tables, not case statements.
You could have something like :
public class Country
{
public List<Region> Regions { get; set; }
public bool IsPostCodeRequiredByDefault { get; set; }
}
public class Region
{
private bool? _isPostCodeRequired;
public Country Country { get; set; }
public bool IsPostCodeRequired
{
get { return _isPostCodeRequired ?? Country.IsPostCodeRequiredByDefault; }
}
}
Which also has the benefit of adressing a secondary "primitive obsession" smell by making region a first-class domain concept instead of just a string.
Try this:
public class PostCodeVerifyMandatory : IPostCodeVerifyMandatory
{
public List<Func<int, string, bool>> Rules { get; private set; }
public PostCodeVerifyMandatory()
{
Rules = new List<Func<int, string, bool>>();
}
public bool IsPostCodeRequired(int countryId, string region)
{
if(Rules == null)
return false;
return (Rules.Any(r => r(countryId, region)));
}
}
You will have to load the ruleset, before using it:
var simpleCountries = new List<int>
{
1, // UK
12, // Australia
29, // Brazil
56, // Brunei
//..
//..
215, //USA
221 //Vietnam
};
var postCodeVerifier = new PostCodeVerifyMandatory();
// 1 rule for simple countries
postCodeVerifier.Rules.Add((id, region) => simpleCountries.Contains(id));
// Special rule for Ireland
postCodeVerifier.Rules.Add((id, region) => id == 232 && region.Equals("Dublin"));
var a = postCodeVerifier.IsPostCodeRequired(232, "Dublin");
or to make it fully data driven (using dictionary as example):
var countries = new Dictionary<int, string>
{
{ 1, null }, // UK
{ 12, null }, // Australia
{ 29, null }, // Brazil
{ 56, null }, // Brunei
//..
//..
{ 215, null }, //USA
{ 221, null }, //Vietnam
{ 232, "Dublin" } // Ireland
};
var postCodeVerifier = new PostCodeVerifyMandatory();
// 1 rule for all
postCodeVerifier.Rules.Add((id, region) =>
countries.ContainsKey(id) &&
(countries[id] ?? region) == region);