How to use enum array parameter on switch case statement? - c#

I had the following method:
private int EarthAttributeValue()
{
var totalValue = 0;
foreach (var item in ItemList)
{
switch (item.Attribute)
{
case AttributeEnum.Earth:
totalValue += item.AttributeValue;
break;
}
}
return totalValue;
}
But i didnt want to create a new method for every value in my Enum, so i'm trying to create a generic method for any attribute, receiving the "target enum" as a parameter.
I've tried this one
private int AttributeValue(AttributeEnum attribute)
{
var totalValue = 0;
foreach (var item in ItemList)
{
switch (item.Attribute)
{
case attribute:
totalValue += item.AttributeValue;
break;
}
}
return totalValue;
}
But it doesn't build, saying that i need a constant value for the case (since the parameter is a variable).
Is there any way to do this? Using Reflection perhaps?
Obs.: I don't want to use an if instead of the switch because the item.Attribute gonna be a list soon, so as the parameter.

I've created this one, replacing the switch by a Linq lambda:
internal int AttributeValue(AttributeEnum attribute)
{
return ItemList
.Where(x => x.Attribute == attribute)
.Sum(x => x.AttributeValue);
}
Thanks #TimSchmelter for the help!

Related

How to use LINQ to filter property which is not null and get its value to run a method?

How do I create LINQ to call DoSomething() only once to reduce these duplicate codes? Note that there are some properties in nameValue which I don't need to do anything about it. DoSomething() is only applied for these 4 properties.
foreach (var nameValue in nameDetails.NameValue)
{
if (nameValue.FirstName != null)
{
DoSomething(nameValue.FirstName)
}
if (nameValue.MaidenName != null)
{
DoSomething(nameValue.MaidenName)
}
if (nameValue.MiddleName != null)
{
DoSomething(nameValue.MiddleName)
}
if (nameValue.Surname != null)
{
DoSomething(nameValue.Surname)
}
}
Move the condition into the function and you can write
foreach (var nameValue in nameDetails.NameValue)
{
DoSomethingMaybe(nameValue.FirstName);
DoSomethingMaybe(nameValue.MaidenName);
DoSomethingMaybe(nameValue.MiddleName);
DoSomethingMaybe(nameValue.Surname);
}
void DoSomethingMaybe(string value)
{
if (value != null)
{
DoSomething(value)
}
}
perfectly readable and no unnecessary overhead, no throwaway objects. Maybe not the answer you expect, but LINQ is not magic, that makes things better just be being there.
You may create an array of *names, then use SelectMany to flatten 2 dimensional array into IEnumerable<string> , e.g.:
var enumerable = nameDetails.NameValue.SelectMany(elem => new[]
{
elem.FirstName,
elem.MaidenName,
elem.MiddleName,
elem.Surname
}).Where(value => value != null);
foreach (var value in enumerable)
{
DoSomething(value);
}
My interpretation of your question is that you want to write DoSomething only once but still want to perform it more than once if more than one name part is not null.
You can refactor your code to this:
var names = nameDetails.NameValue
.SelectMany(nv => new string[] { nv.FirstName, nv.MaidenName, nv.MiddleName, nv.Surname })
.Where(name => name != null);
foreach (var name in names)
{
DoSomething(name);
}
This makes use of SelectMany which allows each item of the source enumerable to be mapped into multiple values which are then flattened into an enumerable of the mapped element value's type.

Adding an array to a method

I have an array of Items called PlayerStartingItems and a method called AddItem. I'm calling the method and then mean to add the array to it so the player has items to start off with.
The code I current have looks like this:
Inventory.AddItem (PlayerStartingItems);
I keep getting an error "Argument 1: cannot convert from 'Shop.Item[]' to 'Shop.Item'"
My method looks like this:
public bool AddItem(Item item)
{
if (mItems == null)
return true;
else
return false;
It uses a class called Item as a parameter and my array uses it as well but to make an array.
How am I properly meant to add my array to my method in this scenario?
You can make use of the params keyword.
That will change your method to:-
public bool AddItem(params Item[] itemList) {
for (int i = 0; i < list.Length; i++) {
if (itemList[i] == null) return true;
else return false
}
}

Check if all List Items have the same member value in C#

I'm searching for a simple and fast way to check if all my Listitems have the same value for a member.
foreach (Item item in MyList)
{
Item.MyMember = <like all other>;
}
EDIT:
I forgot one thing: If one of this members (its a string) is String.Empty and all other items have the same string it should be true too! Sorry i forgot this.
EDIT: Okay, after the new requirement has
bool allSame = !MyList.Select(item => item.MyMember)
.Where(x => !string.IsNullOrEmpty(x))
.Distinct()
.Skip(1)
.Any();
That avoids you having to take a first step of finding one sample value to start with. (And it will still exit as soon as it finds the second value, of course.)
It also only iterates over the sequence once, which may be important if it's not really a repeatable sequence. Not a problem if it's a List<T> of course.
EDIT: To allay concerns over Skip, from the documentation:
If source contains fewer than count elements, an empty IEnumerable<T> is returned. If count is less than or equal to zero, all elements of source are yielded.
Your own solution is simple enough already, but if you wanted to abstract away the loop and write it more expressively, you could use Linq.
bool allSame = MyList.All(item => item.MyMember == someValue);
using System.Linq;
…
if (myList.Any()) // we need to distinguish between empty and non-empty lists
{
var value = myList.First().MyMember;
return myList.All(item => item.MyMember == value);
}
else
{
return true; // or false, if that is more appropriate for an empty list
}
Here is a generic one that works for all version of .NET beginning with 2.0:
public static bool AllSameByProperty<TItem, TProperty>(IEnumerable<TItem> items, Converter<TItem, TProperty> converter)
{
TProperty value = default(TProperty);
bool first = true;
foreach (TItem item in items)
{
if (first)
{
value = converter.Invoke(item);
first = false;
continue;
}
TProperty newValue = converter.Invoke(item);
if(value == null)
{
if(newValue != null)
{
return false;
}
continue;
}
if (!value.Equals(newValue))
{
return false;
}
}
return true;
}
Its usage in C# 2.0:
AllSameByProperty(list, delegate(MyType t) { return t.MyProperty; });
Its usage in C# 3.0:
AllSameByProperty(list, t => t.MyProperty);
Iam doing like so:
Item item = MyList.FirstOrDefault(x=>x.MyMember != valueToMatch);
bool allMembersSame = item == null;
I think this is the shortest and most elegant solution:
bool allSame = list.GroupBy(item => item.TheProperty).Count == 1;

IQueryable remove from the collection, best way?

IQueryable<SomeType> collection = GetCollection();
foreach (var c in collection)
{
//do some complex checking that can't be embedded in a query
//based on results from prev line we want to discard the 'c' object
}
//here I only want the results of collection - the discarded objects
So with that simple code what is the best way to get the results. Should I created a List just before the foreach and insert the objects I want to keep, or is there some other way that would be better to do this type of thing.
I know there are other posts on similar topics but I just don't feel I'm getting what I need out of them.
Edit I tried this
var collection = GetCollection().Where(s =>
{
if (s.property == 1)
{
int num= Number(s);
double avg = Avg(s.x);
if (num > avg)
return true;
else
return false;
}
else return false;
});
I tried this but was given "A lambda expression with a statement body cannot be converted to an expression tree" on compile. Did I not do something right?
//do some complex checking that can't be embedded in a query
I don't get it. You can pass a delegate which can point to a very complex function (Turing-complete) that checks whether you should discard it or not:
var result = GetCollection().AsEnumerable().Where(c => {
// ...
// process "c"
// return true if you want it in the collection
});
If you want, you can refactor it in another function:
var result = GetCollection.Where(FunctionThatChecksToDiscardOrNot);
If you wrap it into another method, you can use yield return and then iterate over the returned collection, like so:
public IEnumerable<SomeType> FindResults(IQueryable<SomeType> collection) {
foreach (var c in collection)
{
if (doComplicatedQuery(c)) {
yield return c;
}
}
}
// elsewhere
foreach (var goodItem in FindResults(GetCollection())) {
// do stuff.
}

Checking if a list is empty with LINQ

What's the "best" (taking both speed and readability into account) way to determine if a list is empty? Even if the list is of type IEnumerable<T> and doesn't have a Count property.
Right now I'm tossing up between this:
if (myList.Count() == 0) { ... }
and this:
if (!myList.Any()) { ... }
My guess is that the second option is faster, since it'll come back with a result as soon as it sees the first item, whereas the second option (for an IEnumerable) will need to visit every item to return the count.
That being said, does the second option look as readable to you? Which would you prefer? Or can you think of a better way to test for an empty list?
Edit #lassevk's response seems to be the most logical, coupled with a bit of runtime checking to use a cached count if possible, like this:
public static bool IsEmpty<T>(this IEnumerable<T> list)
{
if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;
return !list.Any();
}
You could do this:
public static Boolean IsEmpty<T>(this IEnumerable<T> source)
{
if (source == null)
return true; // or throw an exception
return !source.Any();
}
Edit: Note that simply using the .Count method will be fast if the underlying source actually has a fast Count property. A valid optimization above would be to detect a few base types and simply use the .Count property of those, instead of the .Any() approach, but then fall back to .Any() if no guarantee can be made.
I would make one small addition to the code you seem to have settled on: check also for ICollection, as this is implemented even by some non-obsolete generic classes as well (i.e., Queue<T> and Stack<T>). I would also use as instead of is as it's more idiomatic and has been shown to be faster.
public static bool IsEmpty<T>(this IEnumerable<T> list)
{
if (list == null)
{
throw new ArgumentNullException("list");
}
var genericCollection = list as ICollection<T>;
if (genericCollection != null)
{
return genericCollection.Count == 0;
}
var nonGenericCollection = list as ICollection;
if (nonGenericCollection != null)
{
return nonGenericCollection.Count == 0;
}
return !list.Any();
}
LINQ itself must be doing some serious optimization around the Count() method somehow.
Does this surprise you? I imagine that for IList implementations, Count simply reads the number of elements directly while Any has to query the IEnumerable.GetEnumerator method, create an instance and call MoveNext at least once.
/EDIT #Matt:
I can only assume that the Count() extension method for IEnumerable is doing something like this:
Yes, of course it does. This is what I meant. Actually, it uses ICollection instead of IList but the result is the same.
I just wrote up a quick test, try this:
IEnumerable<Object> myList = new List<Object>();
Stopwatch watch = new Stopwatch();
int x;
watch.Start();
for (var i = 0; i <= 1000000; i++)
{
if (myList.Count() == 0) x = i;
}
watch.Stop();
Stopwatch watch2 = new Stopwatch();
watch2.Start();
for (var i = 0; i <= 1000000; i++)
{
if (!myList.Any()) x = i;
}
watch2.Stop();
Console.WriteLine("myList.Count() = " + watch.ElapsedMilliseconds.ToString());
Console.WriteLine("myList.Any() = " + watch2.ElapsedMilliseconds.ToString());
Console.ReadLine();
The second is almost three times slower :)
Trying the stopwatch test again with a Stack or array or other scenarios it really depends on the type of list it seems - because they prove Count to be slower.
So I guess it depends on the type of list you're using!
(Just to point out, I put 2000+ objects in the List and count was still faster, opposite with other types)
List.Count is O(1) according to Microsoft's documentation:
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx
so just use List.Count == 0 it's much faster than a query
This is because it has a data member called Count which is updated any time something is added or removed from the list, so when you call List.Count it doesn't have to iterate through every element to get it, it just returns the data member.
The second option is much quicker if you have multiple items.
Any() returns as soon as 1 item is found.
Count() has to keep going through the entire list.
For instance suppose the enumeration had 1000 items.
Any() would check the first one, then return true.
Count() would return 1000 after traversing the entire enumeration.
This is potentially worse if you use one of the predicate overrides - Count() still has to check every single item, even it there is only one match.
You get used to using the Any one - it does make sense and is readable.
One caveat - if you have a List, rather than just an IEnumerable then use that list's Count property.
#Konrad what surprises me is that in my tests, I'm passing the list into a method that accepts IEnumerable<T>, so the runtime can't optimize it by calling the Count() extension method for IList<T>.
I can only assume that the Count() extension method for IEnumerable is doing something like this:
public static int Count<T>(this IEnumerable<T> list)
{
if (list is IList<T>) return ((IList<T>)list).Count;
int i = 0;
foreach (var t in list) i++;
return i;
}
... in other words, a bit of runtime optimization for the special case of IList<T>.
/EDIT #Konrad +1 mate - you're right about it more likely being on ICollection<T>.
Ok, so what about this one?
public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
return !enumerable.GetEnumerator().MoveNext();
}
EDIT: I've just realized that someone has sketched this solution already. It was mentioned that the Any() method will do this, but why not do it yourself? Regards
Another idea:
if(enumerable.FirstOrDefault() != null)
However I like the Any() approach more.
This was critical to get this to work with Entity Framework:
var genericCollection = list as ICollection<T>;
if (genericCollection != null)
{
//your code
}
If I check with Count() Linq executes a "SELECT COUNT(*).." in the database, but I need to check if the results contains data, I resolved to introducing FirstOrDefault() instead of Count();
Before
var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()
if (cfop.Count() > 0)
{
var itemCfop = cfop.First();
//....
}
After
var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()
var itemCfop = cfop.FirstOrDefault();
if (itemCfop != null)
{
//....
}
private bool NullTest<T>(T[] list, string attribute)
{
bool status = false;
if (list != null)
{
int flag = 0;
var property = GetProperty(list.FirstOrDefault(), attribute);
foreach (T obj in list)
{
if (property.GetValue(obj, null) == null)
flag++;
}
status = flag == 0 ? true : false;
}
return status;
}
public PropertyInfo GetProperty<T>(T obj, string str)
{
Expression<Func<T, string, PropertyInfo>> GetProperty = (TypeObj, Column) => TypeObj.GetType().GetProperty(TypeObj
.GetType().GetProperties().ToList()
.Find(property => property.Name
.ToLower() == Column
.ToLower()).Name.ToString());
return GetProperty.Compile()(obj, str);
}
Here's my implementation of Dan Tao's answer, allowing for a predicate:
public static bool IsEmpty<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) throw new ArgumentNullException();
if (IsCollectionAndEmpty(source)) return true;
return !source.Any(predicate);
}
public static bool IsEmpty<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException();
if (IsCollectionAndEmpty(source)) return true;
return !source.Any();
}
private static bool IsCollectionAndEmpty<TSource>(IEnumerable<TSource> source)
{
var genericCollection = source as ICollection<TSource>;
if (genericCollection != null) return genericCollection.Count == 0;
var nonGenericCollection = source as ICollection;
if (nonGenericCollection != null) return nonGenericCollection.Count == 0;
return false;
}
List<T> li = new List<T>();
(li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty;
myList.ToList().Count == 0. That's all
This extension method works for me:
public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
try
{
enumerable.First();
return false;
}
catch (InvalidOperationException)
{
return true;
}
}

Categories

Resources