Is there a better way to do this?
foreach (var line in lines)
{
bool t01 = line.Model.ToLower() == model;
bool t02 = line.Authority.ToLower() != "unknown";
bool t101 = line.Type.ToLower() == "adcn";
bool t102 = line.Type.ToLower() == "adcn/adv";
bool t103 = line.Type.ToLower() == "bn";
bool t104 = line.Type.ToLower() == "book";
bool t105 = line.Type.ToLower() == "cancel";
bool t106 = line.Type.ToLower() == "cir";
bool t107 = line.Type.ToLower() == "coord sht";
bool t108 = line.Type.ToLower() == "cre";
bool t109 = line.Type.ToLower() == "ddr";
bool t110 = line.Type.ToLower() == "dl";
if (t01 && t02)
if ((t101 || t102 || t103 || t104 || t105 || t106 || t107 || t108 || t109 || t110))
Console.WriteLine(line);
}
It actually goes up to t139. Clipped it for brevity.
It sounds like you need a HashSet<string> for the types:
static readonly HashSet<string> ValidTypes = new HashSet<string>
(StringComparer.OrdinalIgnoreCase)
{
"adcn", "adcn/adv", "bn" ...
};
if (line.Model.Equals(model, StringComparison.OrdinalIgnoreCase) &&
!line.Authority.Equals("unknown", StringComparison.OrdinalIgnoreCase) &&
validTypes.Contains(line.Type))
{
Console.WriteLine(line);
}
That will also be faster than comparing the string for each item individually. Note that although I've used OrdinalIgnoreCase in the above, that may not be what you really want - you may want CurrentCultureIgnoreCase or InvariantCultureIgnoreCase.
(Note that lower-casing strings in order to perform a case-insensitive comparison is a bad idea - particularly if you're just using the default locale to do it in. For example, if you lower-case "MAIL" and your current locale is Turkish, you won't get "mail".)
string[] validTypes = new string[] { "adcn", "adcn/adv", "bn", "book" /*, ...*/ };
foreach (var line in lines)
{
bool t01 = line.Model.ToLower() == model;
bool t02 = line.Authority.ToLower() != "unknown";
if(t01 && t02 && validTypes.Contains(line.Type.ToLower())
Console.WriteLine(line);
}
What do you mean by better? Just off the top of my head, you are evaluating 139 conditions, and then writing the line to the console if any of them are true. It would be more efficient to short circuit if the first one (or the third one, or the fourth, etc) was true and not bother evaluating the rest.
You can do this by storing the evaluation functions in a list:
var cases = new List<Func<Line>>();
cases.Add(l => l.Model.ToLower() == model); //Be careful of the closure here
cases.Add(l => l.Authority.ToLower() != "unknown");
... etc ...
and then evaluating the functions in order for each line, exiting early if the current function returns true:
if (cases.Any(c=>c(line)))
Console.WriteLine(line);
You could use a BitArray
or just plain LINQ
var types = new List<string>{ "adcn", "adcn/adv" }; // etc
if (types.Any(t => t == line.Type.ToLower()))
{
Console.WriteLine(line);
}
Considering your example:
var types = new List<string>{ "adcn", "adcn/adv" }; // etc
foreach (var line in lines)
{
if (types.Any(t => t == line.Type.ToLower()))
{
Console.WriteLine(line);
}
}
Or maybe
var types = new List<string>{ "adcn", "adcn/adv" }; // etc
foreach (var line in lines.Where(line => types.Any(t => t == line.Type.ToLower())))
{
Console.WriteLine(line);
}
Related
I'm getting false positives here. Basically, what I want to happen is only return an error if, for example, the user does not have permission to do Create—but currently it's returning an error even when they have that permission.
User
Permission
Active
12
Create
True
12
Update
False
12
Delete
True
First Example
The following should return true. It does, but it's a false positive because Read, Update, and Delete are not present.
[HasPermission("Create");
Second Example
Should return false, but it is also returning that Read and Delete are not present when I am only asking for Create and Update:
[HasPermission("Create,Update");
Code
string[] permissionValues = permission.Split(",");
Dictionary<string, bool> dict = new Dictionary<string, bool>();
foreach (var item in permissionValues)
{
var permissions = db.AppWarehouseUserPermmissions.Any(w => w.Name == item && w.Controller == controllerName && w.isAcitve == true);
dict.Add(item, permissions);
}
foreach (var item in dict)
{
if (item.Key == "Create" || item.Key == "Read" || item.Key == "Update" || item.Key == "Delete" && item.Value == true)
{
isAuthorised = true;
}
if (item.Key == "Create" || item.Key == "Read" || item.Key=="Update" || item.Key=="Delete" && item.Value==false)
{
_customError.Add(new CutomError() { Message = $"User has no {item.Key} permissions", ErrorCode = item.Key });
}
}
return isAuthorised;
I ended up placing the logic in a unit test and got it to work sometimes the best position is take the code out of the master issue and work on it line by line in the unit test.
This now works as expected but i feel it could be done better.
public void Setup()
{
permissionsList = new List<AppWarehouseUserPermmissions>(){
new AppWarehouseUserPermmissions {
Name="Create",
Action="Create",
Controller="StockItems",
isAcitve=true
//populate other properties
}, new AppWarehouseUserPermmissions {
Name="Update",
Action="Update",
Controller="StockItems",
isAcitve=false
//populate other properties
},new AppWarehouseUserPermmissions {
Name="Delete",
Action="Delete",
Controller="StockItems",
isAcitve=true
//populate other properties
},
new AppWarehouseUserPermmissions
{
Name = "Read",
Action = "Read",
Controller = "Test",
isAcitve = true
//populate other properties
}
};
};
}
[TestCase("Create,Update", "StockItems", 1)]
[TestCase("Delete,Update", "StockItems", 1)]
[TestCase("Read,Update", "StockItems", 2)]
[TestCase("Update", "StockItems", 1)]
public void Vlaidate_Permissions_Returns_Corrrect_Errors(string permission,string controllerName,int expected)
{
bool isAuthorised = false;
List<CutomError> _customError = new List<CutomError>();
string[] permissionValues = permission.Split(",");
Dictionary<string, bool> dict = new Dictionary<string, bool>();
foreach (var item in permissionValues)
{
var permissions = permissionsList.Any(w => w.Name == item && w.Controller == controllerName && w.isAcitve == true);
dict.Add(item, permissions);
}
foreach (var item in dict)
{
if (item.Key == "Create" || item.Key == "Read" || item.Key == "Update" || item.Key == "Delete" && item.Value == true)
{
isAuthorised = true;
}
}
foreach (var item in dict.Where(w => w.Value == false))
{
_customError.Add(new CutomError() { Message = $"Uwas has no {item.Key} permissions", ErrorCode = item.Key });
}
Assert.AreEqual(expected, _customError.Count);
}
}
I have a collection of object type A. I was wondering if I can create another collection, comprising of sub sets of A, such that if A[i].Something == 'a' && A[i+1].Something == 'b', then add it to new collection.
The new collection would be a List of KeyValue pairs such that (Key = A[i], Value = A[i+1])
I wanted to accomplish this using lambda exp. Could someone guide me ?
Since standard Linq doesn't support Lead (Lag) methods (have a look at More Linq if you insist on Linq-like solution), I suggest implementing a simple generator:
private static IEnumerable<KeyValue<MyClass, MyClass>> MakePairs(
IEnumerable<MyClass> source) {
if (null == source)
throw new ArgumentNullException("source");
MyClass prior = default(MyClass);
bool first = true;
foreach (var current in source) {
if (first) {
prior = current;
first = false;
continue;
}
if (prior != null && current != null &&
prior.Something == "A" && current.Something == "B") //TODO: put right condition
yield return new KeyValue(prior, current);
prior = current;
}
}
...
IEnumerable<MyClass> source = ...
var result = MakePairs(source).ToList();
Another way to get the key/value pairs is to zip the collection with all items except the first. Should theoretically work on any enumerable which preserves order. If 'coll' is your source:
coll.Zip(coll.Skip(1), (a1,a2) => new {Key = a1.Something, Value = a2.Something})
To get only for values 'a' and 'b':
coll.Zip(coll.Skip(1), (a1,a2) => new {Key = a1.Something, Value = a2.Something})
.Where(kv=>kv.Key == "a" && kv.Value == "b")
Would this work?
IEnumerable<string> list;
IEnumerable<string> list2 = list.Skip(1);
string test1 = "a";
string test2 = "b";
var result = list
.Zip(list.Skip(1),
(x, y) => Tuple.Create(x, y))
.Where(r => r.Item1 == test1 && r.Item2 == test2)
.ToDictionary(r => r.Item1,
r => r.Item2);
You can use Select, which has an overload to get the index, which is useful in this case to retrieve the next item in your list.
var newCollection = collection.Select
( (a, i) => new
{ A = a
, NextA = (i + 1) < collection.Length ? collection[i + 1] : null
}
);
From there on you can write the predicate you want:
var filteredCollection = newCollection.Where
(x => x.A.Something == "a"
&& x.NextA?.Something == "b"
);
OP has a collection so I started out with an ICollection:
public static IEnumerable<KeyValuePair<A, A>> KeyValueSelecting(ICollection<A> source) {
if (null == source) { throw new ArgumentNullException(nameof(source)); }
for (var i = 0; i < source.Count - 1; i++) {
var firstElement = source.ElementAtOrDefault(i);
if (firstElement?.Something != "A") { yield break; }
var seceondElement = source.ElementAtOrDefault(i + 1);
if (seceondElement?.Something != "B") { yield break; }
yield return new KeyValuePair<A, A>(firstElement, seceondElement);
}
}
Is there a better (nicer) way to write this if statement?
if(string1 == null && string2 == null && string3 == null && string4 == null && string5 == null && string6 == null){...}
Perhaps using the null-coalescing operator(??):
if((string1 ?? string2 ?? string3 ?? string4 ?? string5 ?? string6) == null){ ;}
If all strings are in a collection you can use Linq:
bool allNull = strings.All(s => s == null);
You could put all the strings in a list and use
if(listOfStrings.All(s=>s==null))
At the very least you can put it on multiple lines
if(string1 == null
&& string2 == null
&& string3 == null
&& string4 == null
&& string5 == null
&& string6 == null)
{...}
If you made a function like this:
public static bool AllNull(params string[] strings)
{
return strings.All(s => s == null);
}
Then you could call it like this:
if (AllNull(string1, string2, string3, string4, string5, string6))
{
// ...
}
Actually, you could change AllNull() to work with any reference type, like this:
public static bool AllNull(params object[] objects)
{
return objects.All(s => s == null);
}
string[] strs = new string[] { string1, string2, string3 };
if(strs.All(str => string.IsNullOrEmpty(str))
{
//Do Stuff
}
Or use strs.All(str => str == null) if you don't want to check for empty strings.
Make a IEnumerable of strings (list or array....), then you can use .All()
var myStrings = new List<string>{string1,string2,string3....};
if(myStrings.All(s => s == null))
{
//Do something
}
In case you want to check null or empty, here is another way without arrays:
if (string.Concat(string1, string2, string3, string4, string5).Length == 0)
{
//all null or empty!
}
Well, I don't know if it is nicer or better, or not, you can use IEnumerable.Any method like this;
Determines whether a sequence contains any elements.
List<string> list = new List<string>{"string1","string2","string3", "string4", "string5"};
if(list.Any(n => n == null))
{
}
And you can use Enumerable.All() method like;
Determines whether all elements of a sequence satisfy a condition.
if (Enumerable.All(new string[] { string1, string2, string3, string4, string5 }, s => s == null) )
{
Console.WriteLine("Null");
}
This should do the same:
if (string.IsNullOrEmpty(string1 + string2 + string3 + string4 + string5 + string6)){...}
A User in my system can have Email, Mobile or Phone and based on the values passed I am checking some conditions and then setting the ContactDataStatus (which is an enum) for each. I am then checking the ContactDataStatus to determine whether the provided contact details were valid.
The enum has the following definition
public enum ContactDataStatus
{
ExistsButUnverified = 1,
ExisitsAndVerified = 2,
IsValid = 3,
IsUninitialized = 4
}
I wrote the following if conditions to set isValid variable
isValid = false;
if (emailStatus == ContactDataStatus.IsValid &&
(mobileStatus == ContactDataStatus.IsValid ||
mobileStatus == ContactDataStatus.IsUninitialized)
&& (phoneStatus == ContactDataStatus.IsValid ||
phoneStatus == ContactDataStatus.IsUninitialized))
{
isValid = true;
}
else if (mobileStatus == ContactDataStatus.IsValid &&
(emailStatus == ContactDataStatus.IsValid ||
emailStatus == ContactDataStatus.IsUninitialized) &&
(phoneStatus == ContactDataStatus.IsValid ||
phoneStatus == ContactDataStatus.IsUninitialized))
{
isValid = true;
}
else if (phoneStatus == ContactDataStatus.IsValid &&
(emailStatus == ContactDataStatus.IsValid ||
emailStatus == ContactDataStatus.IsUninitialized) &&
(mobileStatus == ContactDataStatus.IsValid ||
mobileStatus == ContactDataStatus.IsUninitialized))
{
isValid = true;
}
Is there a simpler/shorter way of writing this?
It would help if you told us what the values were for the enum. It sounds like you want at least one of the values to be valid, and all of the values to either be uninitialized or valid. So one way of expressing that would be:
var statuses = new[] { emailStatus, mobileStatus, phoneStatus };
bool valid = statuses.Any(x => x == ContactDataStatus.IsValid) &&
statuses.All(x => x == ContactDataStatus.IsValid ||
x == ContactDataStatus.IsUninitialized);
Or if the status enum is just IsValid, IsUninitialized and (say) IsInvalid, and you knew that the values would actually be in that set, you could write:
var statuses = new[] { emailStatus, mobileStatus, phoneStatus };
bool valid = statuses.Any(x => x == ContactDataStatus.IsValid) &&
statuses.All(x => x != ContactDataStatus.IsInvalid);
Also, I'd suggest that you removed the "is" prefix from each of the enum values - it's just fluff which makes the code harder to read IMO.
var statuses = new[] { emailStatus, mobileStatus, phoneStatus };
bool isValid = statuses
.Any(s => s == ContactDataStatus.IsValid && statuses.Except(new[] { s }).All(o => o == ContactDataStatus.IsValid || o == ContactDataStatus.IsUninitialized));
I have a site where users upload data into it and I only want to update data where properties have been changed. So I am comparing 2 objects of the same type for changes and I need to exclude a few properties such as ModifiedOn which is a date.
Here is my code thus far using reflection:
private bool hasChanges(object OldObject, object newObject)
{
var oldprops = (from p in OldObject.GetType().GetProperties() select p).ToList();
var newprops = (from p in newObject.GetType().GetProperties() select p).ToList();
bool isChanged = false;
foreach (PropertyInfo i in oldprops)
{
if (checkColumnNames(i.Name))
{
var newInfo = (from x in newprops where x.Name == i.Name select x).Single();
var oldVal = i.GetValue(OldObject, null);
var newVal = newInfo.GetValue(newObject, null);
if (newVal == null || oldVal == null)
{
if (newVal == null && oldVal != null)
{
isChanged = true;
return true;
}
if (oldVal == null && newVal != null)
{
isChanged = true;
return true;
}
}
else
{
if (!newVal.Equals(oldVal))
{
isChanged = true;
return true;
}
}
}
}
return isChanged;
}
I ignore certain columns with this method:
private bool checkColumnNames(string colName)
{
if (
colName.ToLower() == "productid" ||
colName.ToLower() == "customerid" ||
colName.ToLower() == "shiptoid" ||
colName.ToLower() == "parentchildid" ||
colName.ToLower() == "categoryitemid" ||
colName.ToLower() == "volumepricingid" ||
colName.ToLower() == "tagid" ||
colName.ToLower() == "specialprice" ||
colName.ToLower() == "productsmodifierid" ||
colName.ToLower() == "modifierlistitemid" ||
colName.ToLower() == "modifierlistid" ||
colName.ToLower() == "categoryitemid" ||
colName.ToLower() == "createdon" ||
colName.ToLower() == "createdby" ||
colName.ToLower() == "modifiedon" ||
colName.ToLower() == "modifiedby" ||
colName.ToLower() == "deletedon" ||
colName.ToLower() == "deletedby" ||
colName.ToLower() == "appendproductmodifiers" ||
colName.ToLower() == "introdate" ||
colName.ToLower() == "id" ||
colName.ToLower() == "discontinued" ||
colName.ToLower() == "stagingcategories"
)
return false;
return true;
}
This has been working very well except for now I have users comparing 50,000+ items in a single upload which is taking a really long time.
Is there a faster way to accomplish this?
Compile and cache your code using expression trees or dynamic methods. You will probably see a 10-100x performance improvement. Your original reflection code to retrieve properties and you can use it as a basis for creating a compiled version.
Example
Here is a snippet of code I use in a framework for reading all of the properties of an object to track state changes. In this scenario, I do not know any of the property names of the object. All of the property values are placed into a StringBuilder.
I've simplified this from my original code; it still compiles, but you may need to tweak it.
private static DynamicMethod CreateChangeTrackingReaderIL( Type type, Type[] types )
{
var method = new DynamicMethod( string.Empty, typeof( string ), new[] { type } );
ILGenerator il = method.GetILGenerator();
LocalBuilder lbInstance = il.DeclareLocal( type );
// place the input parameter of the function onto the evaluation stack
il.Emit( OpCodes.Ldarg_0 );
// store the input value
il.Emit( OpCodes.Stloc, lbInstance );
// declare a StringBuilder
il.Emit( OpCodes.Newobj, typeof( StringBuilder ).GetConstructor( Type.EmptyTypes ) );
foreach( Type t in types )
{
// any logic to retrieve properties can go here...
List<PropertyInfo> properties = __Properties.GetTrackableProperties( t );
foreach( PropertyInfo pi in properties )
{
MethodInfo mi = pi.GetGetMethod();
if( null == mi )
{
continue;
}
il.Emit( OpCodes.Ldloc, lbInstance ); // bring the stored reference onto the eval stack
il.Emit( OpCodes.Callvirt, mi ); // call the appropriate getter method
if( pi.PropertyType.IsValueType )
{
il.Emit( OpCodes.Box, pi.PropertyType ); // box the return value if necessary
}
// append it to the StringBuilder
il.Emit( OpCodes.Callvirt, typeof( StringBuilder ).GetMethod( "Append", new Type[] { typeof( object ) } ) );
}
}
// call ToString() on the StringBuilder
il.Emit( OpCodes.Callvirt, typeof( StringBuilder ).GetMethod( "ToString", Type.EmptyTypes ) );
// return the last value on the eval stack (output of ToString())
il.Emit( OpCodes.Ret );
return method;
}
Note that if you are not familiar with IL generation, most people find expression trees much easier to work with. Either approach has a similar result.
It would certainly be faster if you just used reflection to create and compile a method using the above logic. This should be much faster than reflecting on each object.
Are the objects guaranteed to have the same type? Even if not, you could check them, and if they are the same type, send them to this method:
private bool hasChanges(object OldObject, object newObject)
{
var props = OldObject.GetType().GetProperties();
foreach (PropertyInfo i in props)
{
if (checkColumnNames(i.Name))
{
var oldVal = i.GetValue(OldObject, null);
var newVal = i.GetValue(newObject, null);
if (newVal == null)
{
if (oldVal != null)
{
return true;
}
}
else if (oldVal == null)
{
return true;
}
else if (!newVal.Equals(oldVal))
{
return true;
}
}
}
return false;
}
This is only slightly more efficient than your method. As Tim Medora and PinnyM noted, it would be quicker still to emit code dynamically and cache the result, which would mean that you take the reflection hit only once, rather than once per object.
Note also that according to Best Practices for Using Strings in the .NET Framework, you should be using ToUpper rather than ToLower for your string comparisons, but you should be using String.Equals(string, string, StringComparison) rather than converting the case yourself. That will have one advantage, at least: Equals returns false if the strings are different lengths, so you skip the case conversion. That will save a bit of time as well.
Another thought:
If the objects are of different types, you can still improve your algorithm by joining the properties collections rather than using the repeated linear search you have:
private bool hasChanges(object OldObject, object newObject)
{
var oldprops = OldObject.GetType().GetProperties();
var newprops = newObject.GetType().GetProperties();
var joinedProps = from oldProp in oldprops
join newProp in newProps
on oldProp.Name equals newProp.Name
select new { oldProp, newProp }
foreach (var pair in joinedProps)
{
if (checkColumnNames(pair.oldProp.Name))
{
var oldVal = pair.oldProp.GetValue(OldObject, null);
var newVal = pair.newProp.GetValue(newObject, null);
//etcetera
Final thought (inspired by Tim Schmelter's comment):
Override object.Equals on your classes, and use
private bool HasChanges(object o1, object o2) { return !o1.Equals(o2); }
Sample class:
class SomeClass
{
public string SomeString { get; set; }
public int SomeInt { get; set; }
public DateTime SomeDateTime { get; set; }
public bool Equals(object other)
{
SomeClass other1 = other as SomeClass;
if (other1 != null)
return other1.SomeInt.Equals(SomeInt)
&& other1.SomeDateTime.Equals(SomeDateTime)
&& other1.SomeString.Equals(SomeString); //or whatever string equality check you prefer
//possibly check for other types here, if necessary
return false;
}
}