In the following example I would like to add flavours that start with "APPLE" to a ComboBox on a form. When the enums have unique values it works fine; however, in my example two enums PINEAPPLE_PEACH and APPLE_ORANGE both have a value of 1 and this messes up the results.
Is it erroneous to have two enums with the same value and, if so, how can I change my code to get consistent results?
public enum Flavour
{
APPLE_PEACH = 0,
PINEAPPLE_PEACH = 1,
APPLE_ORANGE = 1,
APPLE_BANANA = 3,
PINEAPPLE_GRAPE = 4
}
private void AddFlavours()
{
foreach (Flavour flavour in Enum.GetValues(typeof(Flavour)))
{
string flavourName = Enum.GetName(typeof(Flavour), flavour);
if (flavourName.StartsWith("APPLE"))
{
myComboBox.Items.Add(flavour);
}
}
}
With Linq, you may use this:
foreach (string flavourName in Enum.GetNames(typeof(Flavour)).Where(s => s.StartsWith("APPLE")))
{
myComboBox.Items.Add(flavourName);
}
You can use Enum.GetNames instead of GetValues. It would be something like this (not tested):
foreach (string flavourName in Enum.GetNames(typeof(Flavour)))
{
if (flavourName.StartsWith("APPLE"))
{
myComboBox.Items.Add(Enum.Parse(typeof(flavour), flavourName));
}
}
Related
In my WPF program, I have an area of C# code that is quite repetitive, and it looks like this:
labelFirst = EnglishPicker.SelectedColorText.Substring(0, 1);
labelLast = EnglishPicker.SelectedColorText.Substring(3, 6);
label = labelFirst + labelLast;
UpdateSetting("English", label);
labelFirst = PhotographyPicker.SelectedColorText.Substring(0, 1);
labelLast = PhotographyPicker.SelectedColorText.Substring(3, 6);
label = labelFirst + labelLast;
UpdateSetting("Photography", label);
So I had a thought, is it possible to put this in a for loop something like this:
string[] names = {"English","Photography"};
foreach (string name in names)
{
labelFirst = (name +"Picker").SelectedColorText.Substring(0, 1);
}
Solution for those seeking help
Using a SharedMethod, I was able to shorten the code down as EnglishPicker and PhotographyPicker are the same class. Code:
private void GetPickerLabel(PickerClass picker){
labelFirst = picker.SelectedColorText.Substring(0,1);
labelLast = picker.SelectedColorText.Substring(3,6);
return labelFirst + labelLast;
}
UpdateSetting("English",GetPickerLabel(EnglishPicker));
UpdateSetting("Photography",GetPickerLabel(PhotographyPicker));
Thanks for those who helped me. :)
I think you almost nailed the simplest approach to this in your question, but using strings to refer to the pickers can break at run-time.
Try this instead:
Picker[] pickers = { EnglishPicker, PhotographyPicker};
foreach (Picker picker in pickers)
{
labelFirst = picker.SelectedColorText.Substring(0, 1);
}
The advantage here is that you have compile-time correctness and you can explicitly include or exclude any pickers you like.
It would be easy to turn the control back into text via the .Name property to get the setting updated.
Providing EnglishPicker and PhotographyPicker are properties:
You can use Reflection:
IEnumerable<string> names = new [] { "English", "Photography" };
foreach (string name in names)
{
// Build the property name
string propertyName = String.Concat(name, "Picker");
// Get Picker instance by locating the correct property
object picker = this.GetType().GetProperty(propertyName).GetValue(this);
// Get the SelectedColorText value from that instance
string colorText = (string) picker.GetType().GetProperty("SelectedColorText").GetValue(picker);
string first = colorText.Substring(0, 1);
string last = colorText.Substring(3, 6);
string label = String.Concat(first, last);
// Call UpdateSetting
UpdateSetting(name, label);
}
Flexibility comes with a price.
Everything, from the property lookup to the method invocation will be done in runtime, so watch out! It'll compile fine but it might break when you run it.
For instance, if you were to mistype "Enlgish" instead of "English", the code would compile perfectly. But, at runtime, the code will try to locate EnlgishPicker property and it'll fail.
Also, the above code is not defensive at all.
You should update it to include null checks to avoid potential exceptions.
How about an interface? No reflection needed at all. That's what they're for.
public interface ISelectedColorContainer {
string SelectedColorText {get;}
}
An Implementation
(I have no idea what your classes look like, but that's why we use interfaces!)
public class EnglishPickerImplementation:ISelectedColorContainer {
public string SelectedColorText {get {return "sunflower";}}
}
public class PhotographyPickerImplementation:ISelectedColorContainer {
public string SelectedColorText {get {return "chartreuse";}}
}
So you'd have a function that looks like this:
void SomeMethod(){
ISelectedColorContainer items = new ISelectedColorContainer[] {
new EnglishPickerImplementation(), new PhotographyPickerImplementation()};
foreach(var item in items){
var labelFirst = UpdateSetting(item);
var labelLast = item.SelectedColorText.Substring(3, 6);
var label = labelFirst + labelLast;
}
}
string UpdateSetting(ISelectedColorContainer input){
return input.SelectedColorText.Substring(0, 1);
}
Sorry if there are slight syntax errors, I didn't put this in an IDE.
Instead of a reflection-based solution, I would simply refactor the code to move the shared code in a separate method.
Example:
SharedMethod(EnglishPicker, "English");
SharedMethod(PhotographyPicker, "Photography");
// ...
private void SharedMethod(PickerClass picker, string settingName)
{
var labelFirst = picker.SelectedColorText.Substring(0, 1);
var labelLast = picker.SelectedColorText.Substring(3, 6);
var label = labelFirst + labelLast;
UpdateSetting(settingName, label);
}
I made a simple dynamic object:
class Row
{
Dictionary<string, object> properties = new Dictionary<string, object>();
private int rIndex;
public Row(int rIndex)
{
this.rIndex = rIndex;
}
public object this[string name]
{
get
{
if (properties.ContainsKey(name))
{
return properties[name];
}
return null;
}
set
{
properties[name] = value;
}
}
public int RIndex
{
get { return rIndex; }
set { rIndex = value; }
}
}
I get the coloumn that i use to group from a configuration file.
Group by different value for example like this :
var t = lst.GroupBy(x => new { x1 = x["job"], x2 = x["schema"], x3 = x["line"], x4 = x["plant"], x5 = x["mod"], x6 = x["tag"] }).Select(g => new { g.Key.x1, g.Key.x2, g.Key.x3, g.Key.x4, g.Key.x5, g.Key.x6, });
this works well but i think it's too static.
How can i implement a dynamic Group by clause?
Is it possible, inside the Group By clause, get the Dictionary key value that is inside the dynamic object?
Thanks all in advance.
Okay, I think it probably makes sense to have something like a RowView class, which contains a reference to a Row, and the properties you're interested in (a subset of the row's properties). You can then make it implement IEquatable<RowView>, such that two RowView objects are equal if and only if:
They contain the same properties, in the same order
The property values are the same in the rows they refer to
For convenience, I'd probably add a CreateView(IEnumerable<string> properties) method to Row, so you can call:
var t = lst.GroupBy(x => x.CreateView(groupingProperties));
(I'd also advise using an automatically-implemented property for RIndex to simplify the code - and ideally rename it, as it's not clear what it's for at the moment.)
I have a list that contains 3 items, two of type_1, and one of type_2. I want to return a second list that contains the type and number of that type that exists. When stepping through the breakpoints set at the foreach loop, the IF statement is never true. I assume there is something wrong with my attempt to use Contains() method.
The output should be something like:
type_1 2
type_2 1
Instead, it evaluates as:
type_1 1
type_1 1
type_2 1
Is my use of Contains() not correct?
public List<item_count> QueryGraphListingsNewAccountReport()
List<item> result = new List<items>();
var type_item1 = new item { account_type = "Type_1" };
var type_item2 = new item { account_type = "Type_1" };
var type_item3 = new item { account_type = "Type_2" };
result.Add(type_item1);
result.Add(type_item2);
result.Add(type_item3);
//Create a empty list that will hold the account_type AND a count of how many of that type exists:
List<item_count> result_count = new List<item_count>();
foreach (var item in result)
{
if (result_count.Contains(new item_count { account_type = item.account_type, count = 1 } ) == true)
{
var result_item = result_count.Find(x => x.account_type == item.account_type);
result_item.count += 1;
result_count.Add(result_item);
}
else
{
var result_item = new item_count { account_type = item.account_type, count = 1 };
result_count.Add(result_item);
}
}
return result_count;
}
public class item
{
public string account_type { get; set; }
}
public class item_count
{
public int count {get; set;}
public string account_type { get; set; }
}
I think your problem is that you don't want to use contains at all. You are creating a new object in your contains statement and, obviously, it isn't contained in your list already because you only just created it. The comparison is comparing references, not values.
Why not just use the find statement that you do in the next line instead? If it returns null, then you know there isn't an item already with that type.
So you could do something like this:
var result_item = result_count.Find(x => x.account_type == item.account_type);
if (result_item != null)
{
result_item.count++;
// note here you don't need to add it back to the list!
}
else
{
// create your new result_item here and add it to your list.
}
Note: Find is o(n), so this might not scale well if you have a really large set of types. In that case, you might be better off with Saeed's suggestion of grouping.
You can do:
myList.GroupBy(x=>x.type).Select(x=>new {x.Key, x.Count()});
If you want use for loop, it's better to use linq Count function to achieve this, If you want use Contains you should implement equal operator as the way you used.
I have a list of Enums like the following:
public enum Evaluation : int
{
//Section 1
S1_1_1 = 579,
S1_1_2 = 584,
S1_1_3 = 589,
S1_1_4 = 594,
S1_1_5 = 599,
S1_1_6 = 604,
//Section 2
S1_2_1 = 610,
S1_2_2 = 615,
S1_2_3 = 620,
S1_2_4 = 625,
S1_2_5 = 630,
};
I want to iterate each section and use the values dynamically
int S1Count = 6;
for (int i = 1; i <= S1Count; i++)
{
VoteCount += string.IsNullOrEmpty(this.GetEvaluationValue(FormID, Evaluation.S1_1_ + i)) ? 0 : 1;
}
How can I achieve that? Thanks.
Sorry, my mistake. I tried to get the value from the database by using enum values which are IDs and I have to calculate counts, average for each section.
You can use Enum.Parse to do what you want I think though I don't reccomend it.
To use enum.Parse you'd just need to do something like:
Enum.Parse(typeof(Evaluation), String.Format("S1_1_{0}",i));
This does point at you using some dodgy methodology though. As I said in comments above you would be better off with a data structure allowing you to have sections and their contents easily differentiated. You can do this with either custom classes or maybe just a dictionary of Lists of ints...
Dictionary<int, List<int>> SectionContents;
and use it like:
foreach(int id in SectionContents[sectionNumber])
{
VoteCount += string.IsNullOrEmpty(this.GetEvaluationValue(FormID, id)) ? 0 : 1;
}
(I don't vouch for what's in the foreach, I'm just demonstrating how a dictionary of a list of ints could work).
Creating the Dictionary is easy enough and doesn't require enums. And if this is database stuff could easily be generated through a database query to get the IDs and what sections they are in and then create the data structure.
This will do it
public class Program
{
public static void Main()
{
foreach (FieldInfo fInfo in typeof(Evaluation).GetFields(BindingFlags.Public | BindingFlags.Static))
{
Console.WriteLine("Evaluation." + fInfo.Name);
}
Console.ReadLine();
}
}
I have a flagged enum and need to retrieve the names of all values set on it.
I am currently taking advantage of the enum's ToString() method which returns the elements comma-separated.
public void SetRoles(Enums.Roles role)
{
IList<Entities.Role> roleList = role.ToString("G").Split(',')
.Select(r => new Entities.Role(r.Trim()))
.ToList();
...
}
I'm sure there must be a better way than this.
Try this:
public void SetRoles(Enums.Roles role)
{
List<string> result = new List<string>();
foreach(Roles r in Enum.GetValues(typeof(Roles)))
{
if ((role & r) != 0) result.Add(r.ToString());
}
}
If you genuinely just want the strings, can't get much simpler than:
string[] flags = role.ToString().Split(',');
This is simpler than using LINQ and is still just a single line of code.
Or if you want a list instead of an array as in the sample in the question you can convert the array into a list:
List<string> flags = new List<string>(role.ToString().Split(','));
In my case I needed a generic solution and came up with this:
value.ToString().Split(',').Select(flag => (T)Enum.Parse(typeof(T), flag)).ToList();
Enum.Parse will handle the concatenated values outputted by ToString just fine. Proof using the Immediate window:
? System.Enum.Parse(typeof(System.AttributeTargets), "Class, Enum")
Class | Enum
(the second line is the output, which is different in the debugger/immediate window from the generic Enum.ToString() output).
List<string> GetRoleNames(Roles roles) =>
Enum.GetValues(typeof(Roles))
.Cast<Roles>()
.Where(role => roles.HasFlag(role))
.Select(role => role.ToString())
.ToList();
void TestRoleSelection()
{
var selectedRoles = (Roles)6;
var roleNames = GetRoleNames(selectedRoles);
Console.WriteLine(string.Join(",", roleNames));
// Output: Admin,User
}
[Flags]
enum Roles
{
SuperAdmin = 1,
Admin = 2,
User = 4,
Anonymous = 8
}
Why do you need a list? Everything is already stored in the flags:
[Flags]
enum Roles
{
Read = 0x1,
Write = 0x2,
Delete = 0x4,
}
Then assign roles:
var roles = Roles.Read | Roles.Write;
And whenever you need to check if a given role has been you don't need to look in a list, but simply look in the roles enumeration:
if ((roles & Roles.Read) == Roles.Read)
{
// The user has read permission
}
if ((roles & Roles.Write) == Roles.Write)
{
// The user has write permission
}
Similar answer to Mick's but puts the operations into extensions and fixes/cleans up the extra space character (from the split).
Also as a bonus if the enum has a _ in it, the code changes it to a space.
public static class EnumExtensions
{
// Take anded flag enum and extract the cleaned string values.
public static List<string> ToComparableStrings(this Enum eNum)
=> eNum.ToString()
.Split(',')
.Select(str => str.ToCleanString())
.ToList();
// Take an individual enum and report the textual value.
public static string ToComparableString(this Enum eNum)
=> eNum.ToString()
.ToCleanString();
// Remove any spaces due to split and if `_` found change it to space.
public static string ToCleanString(this string str)
=> str.Replace(" ", string.Empty)
.Replace('_', ' ');
}
Usage
var single = PivotFilter.Dollars_Only;
var multiple = PivotFilter.Dollars_Only | PivotFilter.Non_Productive;
// These calls return:
single.ToComparableString() // "Dollars Only"
multiple.ToComparableString() // "Non Productive,Dollars Only"
multiple.ToComparableStrings() // List<string>() { "Non Productive", "Dollars Only" }
Enum for Usage
[Flags]
// Define other methods, classes and namespaces here
public enum PivotFilter
{
Agency = 1,
Regular = 2,
Overtime = 4,
Non_Productive = 8,
Dollars_Only = 16,
Ignore = 32
}
Turning a flagged enum to a list might not be as straight forward as it looks. Take the following enum for example:
[Flags]
enum MenuItems
{
None = 0,
Pizza = 1,
Fries = 2,
Pancakes = 4,
Meatballs = 8,
Pasta = 16,
StuffWithP = Pizza | Pancakes | Pasta,
All = Pizza | Fries | Pancakes | Meatballs | Pasta | StuffWithP
};
If we have the value StuffWithP, what do we want in the list? StuffWithP or Pizza, Pancakes, Pasta? I had a use case in witch I needed to "deconstruct" the enum value to the invidual flags and put those in a list. I came up with the following:
public static string[] DeconstructFlags(Enum items)
{
if (items.GetType().GetCustomAttribute<FlagsAttribute>() == null)
{
throw new ArgumentException("Enum has no [Flags] attribute.", nameof(items));
}
// no value, no list
var itemsValue = (int)(object)items;
if (itemsValue == 0) return Array.Empty<string>();
var result = new List<string>();
foreach (var item in Enum.GetValues(items.GetType()))
{
if(item == null) continue;
var value = (int)item;
// skip combined flags
if (!BitOperations.IsPow2(value))
{
continue;
}
if (items.HasFlag((Enum)item))
{
result.Add(item.ToString() ?? "");
}
}
return result.ToArray();
}
I don't know if it is the most efficient, but it skips those combined flags nicely. Wrote some more on my blog: Deconstructing a [Flags] enum.
F# version
module Enum =
let inline flagsToList< ^e when ^e: equality and ^e: (static member (&&&): ^e * ^e -> ^e)> value =
Enum.GetValues(typeof<^e>)
|> Seq.cast<^e>
|> Seq.where (fun case -> case &&& value = case)
|> Seq.toList