I have this code which i want to change:
foreach (DirectoryInfo path in currDirs) {
if (!newDirs.Contains(path)) {
MyLog.WriteToLog("Folder not Found: "+path.Name + "in New Folder. ",MyLog.Messages.Warning);
currNoPairs.Add(path);
}
}
In the If part i don't want to check the path i want to check the path.Name.
So how can i use the Contains method on the properties.
the goal is to sort out all folders that have not the same name in the list of Current Directory List and New Directory List.
See - IEnumerable<T>.Contains with predicate
Those functions that take "predicates" (boolean functions that signify a match) will let you do more complex checks. In this case, you can use them to compare sub-properties instead of the top-level objects.
The new code will look something like this:
foreach (DirectoryInfo path in currDirs) {
if (!newDirs.Any(newDir => newDir.Name == path.Name)) {
// TODO: print your error message here
currNoPairs.Add(path.Name);
}
}
In reply to your comment:
Okay i understood, but whats the diffrence between any and contains then?
List<T>.Contains
This method goes through each item in the list, seeing if that item is equal to the value you passed in.
The code for this method looks a little like this (simplified here for illustration):
for(var item in yourList) {
if(item.Equals(itemYouPassedIn) {
return true;
}
}
return false;
As you see, it can only compare top-level items. It doesn't check sub-properties, unless you are using a custom type that overrides the default Equals method. Since you're using the built in DirectoryInfo types, you can't override this Equals behavior without making a custom derived class. Since there's easier ways to do this, I wouldn't recommend this approach unless you need to do it in a ton of different places.
IEnumerable<T>.Any
This method goes through each item in the list, and then passes that item to the "predicate" function you passed in.
The code for this method looks a little like this (simplified for illustration):
for(var item in yourList) {
if(isAMatch(item)) { // Note that `isAMatch` is the function you pass in to `Any`
return true;
}
}
return false;
Your predicate function can be as complicated as you want it to be, but in this case, you'd just use it to check if the sub-properties are equal.
// This bit of code defines a function with no name (a "lambda" function).
// We call it a "predicate" because it returns a bool, and is used to find matches
newDir => newDir.Name == path.Name
// Here's how it might look like if it were defined as a normal function -
// this won't quite work in reality cause `path` is passed in by a different means,
// but hopefully it makes the lambda syntax slightly more clear
bool IsAMatch(DirectoryInfo newDir) {
return newDir.Name == path.Name;
}
Since you can customize this predicate every place that you use it, this could be a better tactic. I'd recommend this style until you are doing this exact check in a bunch of places in your code, in which case a custom class might be better.
Here is how you check for property Any
foreach (DirectoryInfo path in currDirs) {
if (!newDirs.Any(dir => dir.FullName == path.FullName)) {
MyLog.WriteToLog("Folder not Found: "+path.Name + "in New Folder. ",MyLog.Messages.Warning);
currNoPairs.Add(path);
}
}
And by the way, your code could be written in a better way like this
var currDirsConcrete = currDirs.ToArray();
var pathsNotFound = "Following paths were not found \r\n " + string.Join("\r\n", currDirsConcrete.Where(d => d.FullName != path.FullName).ToArray());
var pathsFound = currDirsConcrete.Where(d => d.FullName == path.FullName).ToArray();
MyLog.WriteToLog(pathsNotFound, MyLog.Messages.Warning);
Note: You can skip the first line currDirsConcrete if your currDirs is already an array or a list. I did this to avoid redetermining the enumerable.
I would use linq with except and implement a DirComparator
List<DirectoryInfo> resultExcept = currDirs.Except(newDirs, new DirComparator()).ToList();
Here the IEqualityComparer<DirectoryInfo>:
public class DirComparator : IEqualityComparer<DirectoryInfo> {
public bool Equals(DirectoryInfo x, DirectoryInfo y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the products' properties are equal.
return x.Name.equals(y.Name);
}
public int GetHashCode(DirectoryInfo dir)
{
//Check whether the object is null
if (Object.ReferenceEquals(dir, null)) return 0;
//Get hash code for the Name field if it is not null.
return dir.Name == null ? 0 : dir.Name.GetHashCode();
}
}
you could also use intersect if you want it the other way around.
Related
I have 2 classes one called Record and one called Location
public class Record
{
public string Id { get; set; }
}
public class Location
{
public string LocationId { get; set; }
public bool isDefault { get; set; }
}
Now I need to check whether in a list of Locations whether the given ID from Record exists in the List of Locations. For example this is my code so far for handling. If field.Id does not exist in the current list then it should create a new Location and add it to the list.
foreach (Record field in inputs)
{
locationResponse = await myLocationCmd.GetLocation(locationInput).ConfigureAwait(false);
foreach (Location locations in locationResponse.Locations)
{
if (locations.IsDefault == true)
{
this.BadRequest();
}
else if (locations.IsDefault == false && // Check whether field.Id exists in locationResponse.Locations)
{
locationInput.Verb = CmdletVerb.Set.ToString();
}
else if (locations.IsDefault == false && // Check whether field.Id does not exist in the locationResponse.Locations)
{
locationInput.Verb = CmdletVerb.New.ToString();
}
}
Currently I tried doing locationResponse.Locations.Contains(field.Id) but this produces a type error. Does anyone know the correct way to do this?
If I understood correctly, you need to loop over every object to check the ID
locationResponse.Locations.Contains(field.Id) gives a type error because that is a list of Locations, not a list of Strings
locationResponse = await myLocationCmd.GetLocation(locationInput).ConfigureAwait(false);
foreach (Record field in inputs) {
var idExists = false;
foreach (Location loc in locationResponse.Locations)
idExists = loc.LocationId == field.Id;
if (idExists) break;
}
Console.WriteLine(idExists);
}
I would be cautious about the size of the inputs list, though, because you are looking over the same locations list more than once for every field, which is wasted iterations
The shortest fix based on the given code would be using:
bool idExistsInList = locationResponse.Locations.Any(location => location.LocationId == field.Id);
And then you can use idExistsInList and !idExistsInList (respectively) in your two comments.
However, there are several further improvements to be made:
There is a bug in your code. Your inner foreach keeps overwriting the same value and will effectively only keep the value that is set in the last loop of the foreach.
The exact same bug happens for your outer foreach. The value of locationInput.Verb is effectively defined by comparing the last element of both lists, and all other evalutation are effectively overwritten.
In general, favor LINQ over manual foreach logic. It enhances readability and cuts down on boilerplating.
Assuming this is the only logic in the outer foreach, that can also be simplified using LINQ.
For every input field, you fetch the same locationResponse. It makes no sense to do this for every field if the specific field value isn't actually used to fetch the object.
There's no need for the third if evaluation. When the first two evaluations have failed, then the third one is always going to be true.
== true can always be omitted.
== false should generally be refactored to using !
When you always set the same field/property, but a boolean value decided if you set it to one value or another, favor using the ternary myBool ? valueIfTrue : valueIfFalse.
You can extract this logic outside of the foreach loops:
var locationResponse = await myLocationCmd.GetLocation(locationInput).ConfigureAwait(false);
if(locationResponse.Locations.Any(location => location.IsDefault))
this.BadRequest();
var locationIds = locationResponse.Locations.Select(location => location.LocationId).ToList();
How to handle the foreach loops is unclear, as I'm not sure whether you're trying to find if any field (= at least one) has a match in the location list, or whether every field has a match in the location list.
If you're trying to find if any field (= at least one) has a match in the location list:
bool matchFound = inputs.Any(field => locationIds.Contains(field.Id);
If you're trying to find if every field has a match in the location list:
bool matchFound = inputs.All(field => locationIds.Contains(field.Id);
Any returns true if at least one field has a match, All only returns true if all fields have a match.
In either case, you can then continue on with:
locationInput.Verb = matchFound
? CmdletVerb.Set.ToString()
: CmdletVerb.New.ToString();
And that accomplishes the same as your code, but with less redundancy and higher readability.
I've seen plenty of examples of how to use generic Objects in C#, but can I have a generic property for a static object?
I have 2 distinct lists of Providers:
List<Providers> OldProviders
List<Providers> NewProviders
OldProviders has providers from the database, and NewProviders has providers the user has selected in the View. If any OldProviders aren't in NewProviders, I want to remove them from the database. If any NewProviders aren't in OldProviders, I want to add them to the database. Simple.
One problem:
Provider has 2 properties that correspond to foreign keys important for comparison: StaffID and LicenseID (both integers)
Depending on the choice the user makes, I only care about one of these IDs.
Currently I have 2 sets of helper methods which are completely identical (6 total helpers), except for whether they compare using StaffID or LicenseID. It technically works, but I don't want to maintain it.
Here's an example:
private bool DeleteOldStaffProviders(List<Provider> oldSelectedStaff, List<Provider> newSelectedStaff)
{
foreach (var oldSelected in oldSelectedStaff)
{
bool remove = newSelectedStaff.SingleOrDefault(n => n.StaffID == oldSelected.StaffID) == null;
if (remove)
{
//<remove from database, return false if failure>
}
}
return true;
}
Is there a way I can rewrite this method with a generic parameter reference?
i.e.
newSelectedStaff.SingleOrDefault(n => n.T == oldSelected.T)
where T is either LicenseID or StaffID.
I've seen a lot of examples of people using LINQ and reflection for things kinda similar to this, but I can't seem to figure it out. If you could baby me through it, that would be appreciated.
Yes, use another parameter of type Func<Provider,int> to act as the selector:
private bool DeleteOldStaffProviders(List<Provider> oldSelectedStaff, List<Provider> newSelectedStaff, Func<Provider,Provider,bool> selector)
{
foreach (var oldSelected in oldSelectedStaff)
{
bool remove = newSelectedStaff.SingleOrDefault(n => selector(n) == selector(oldSelected)) == null;
if (remove)
{
//<remove from database, return false if failure>
}
}
return true;
}
usage
DeleteOldStaffProviders(oldStaff, newStaff, x => x.StaffID);
DeleteOldStaffProviders(oldStaff, newStaff, x => x.LicenseID);
You could do it another way - but I think its more clunky. Pass in a Func<Provider,Provider,bool> like this:
DeleteOldStaffProviders(oldStaff, newStaff, (o,n) => o.StaffID == n.StaffID);
And change a line to:
bool remove = newSelectedStaff.SingleOrDefault(n => selector(n,oldSelected))
I need to compare two objects of the same type but with a lot of prop
return string.Equals(x.Name, y.Name) &&
x.Price == y.Price &&
string.Equals(x.Species, y.Species) &&
x.AgeEnforced == y.AgeEnforced &&
x.MinimumAge == y.MinimumAge &&
string.Equals(x.WeightRange, y.WeightRange) &&
x.MinimumWeight == y.MinimumWeight
The list goes on up to 14 props
A hint on how can I lose && operator?
If your class has 14 properties and all of them have to be compared to ensure equality, all of these checks have to be made somewhere. No matter if you put them into a (maybe static) helper method, the Equals-method, a class that implements IEqualityComparer (IMHO the best choice), an extension method or whatever else comes to your mind, you have to write down all of these checks once.
Instead of writing these checks on yourself you can use some reflection stuff (or a library that makes use of it) by iterating over all properties, but that's normally not the best choice, cause it is slower and equality checks are tend to be called very often (sort, hashset, etc.). So take the pain and write the checks down.
If you think these are too many checks, then maybe your object is to fat and has to much properties. Maybe it needs a deeper hierarchy by using classes which are representing a part of the whole thing and you have a top class that has as property a child class and then you write the compare method for each child class individually and the top class just calls these comparers for its child classes.
Answer to A hint on how can i lose && operator?
if (string.Equals(x.Name, y.Name) == false) return false;
if (x.Price != y.Price) return false
// ... others
return true;
This code must be somewhere. If this is only place where this code exist this is ok in my opinion. Bu if it exists in many places then you can move it to some helper fuction or override Equals() method in your object. MSDN info.
Example how to oveeride and use Equals():
public class MyObject
{
public string Name { get; set; }
public decimal Price { get; set; }
public override bool Equals(object obj)
{
MyObject o = obj as MyObject;
if (o == null)
return false;
if (this.Name != o.Name)
return false;
return true;
}
/// <summary>
/// Serves as the default hash function.
/// </summary>
/// <returns>
/// A hash code for the current object.
/// </returns>
public override int GetHashCode()
{
return base.GetHashCode();
}
}
public class Program
{
static public void Main()
{
MyObject o1 = new MyObject()
{
Name = "a",
Price = 1
};
MyObject o2 = new MyObject()
{
Name = "b",
Price = 1
};
MyObject o3 = new MyObject()
{
Name = "a",
Price = 1
};
Console.WriteLine("o1 == o2: {0}", o1.Equals(o2));
Console.WriteLine("o1 == o3: {0}", o1.Equals(o3));
Console.ReadKey();
}
}
Result is:
o1 == o2: False
o1 == o3: True
For me, && or || are pretty short already. Your problem lies not in the operator itself, but in the way you are doing the comparison of 14+ properties.
Instead of writing all the property-comparisons by hand, you can try automating it.
var props_string = new List<Func<MyObject, string>>();
props_string.Add(foo => foo.Name);
props_string.Add(foo => foo.Species);
//..
var props_double = new List<Func<MyObject, double>>();
props_double.Add(foo => foo.Price);
//..
Now, having them listed, you can ran a loop over it
MyObject first = ...;
MyObject second = ...;
bool areEqual = true;
foreach(var prop in props_string)
if(!string.Equals(prop(first), prop(second)))
areEqual = false;
foreach(var prop in props_double)
if(prop(first) != prop(second))
areEqual = false;
return areEqual;
The positive side is that even if there were 100 properties, one or two (or more) loops would fly over it.
Please note that this code is not optimized and pretty naive.
More natural would be a fast-exit on first difference:
foreach(var prop in props_double)
if(prop(first) != prop(second))
return false;
..and/or observing that string.equals and double!=double can all be packed as the same object.Equals method (so you don't need separate props_string, props_double etc)
..and/or observing that you don't really need to list all properties, you can get them from Reflection, so you don't need to 'define' the 100 properties in these lists. (But then, you often have some special properties that you don't want to include.. so your reflection-way would need a way to detect and skip them..)
..and/or observing that if you used struct instead of class you would get full memberwise object.Equals comparison for free. (But then, structs differ from classes in many other things..)
..and/or(...)
All these 'optimizations' are highly dependent on what you have in the rest of the 14+ comparisons. It looks fun and smart at first, but the more the types of properties vary and the more types of comparisons you need to do (string.equals with case-insensitive maybe?) the worse it will get. Sometimes 14 times &&, or 14 times if(x==y)return false isn't really that bad.
Please don't get me wrong: I do not advise you to use the way I show here. I just show you that it is possible. Don't use it unless absolutely relevant and necessary. Doing it this way may cost you much more time than you would want to.
This seems as a correct way how to do the compare.
If your aim is to be able to easily check if two different instances are equal you can override Equals() and GetHashCode().
You will be able to do:
x.Equals(y)
You can also override operator ==. In which case you can do:
x == y
Check MSDN for details and example.
Also there is no reason for you (unless you want to use any extra parameters) to use
string.Equals(x.Name, y.Name)
use simply
x.Name == y.Name
Here's what I'm trying to do. I'm querying an XML file using LINQ to XML, which gives me an IEnumerable<T> object, where T is my "Village" class, filled with the results of this query. Some results are duplicated, so I would like to perform a Distinct() on the IEnumerable object, like so:
public IEnumerable<Village> GetAllAlliances()
{
try
{
IEnumerable<Village> alliances =
from alliance in xmlDoc.Elements("Village")
where alliance.Element("AllianceName").Value != String.Empty
orderby alliance.Element("AllianceName").Value
select new Village
{
AllianceName = alliance.Element("AllianceName").Value
};
// TODO: make it work...
return alliances.Distinct(new AllianceComparer());
}
catch (Exception ex)
{
throw new Exception("GetAllAlliances", ex);
}
}
As the default comparer would not work for the Village object, I implemented a custom one, as seen here in the AllianceComparer class:
public class AllianceComparer : IEqualityComparer<Village>
{
#region IEqualityComparer<Village> Members
bool IEqualityComparer<Village>.Equals(Village x, Village y)
{
// Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y))
return true;
// Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.AllianceName == y.AllianceName;
}
int IEqualityComparer<Village>.GetHashCode(Village obj)
{
return obj.GetHashCode();
}
#endregion
}
The Distinct() method doesn't work, as I have exactly the same number of results with or without it. Another thing, and I don't know if it's usually possible, but I cannot step into AllianceComparer.Equals() to see what could be the problem.
I've found examples of this on the Internet, but I can't seem to make my implementation work.
Hopefully, someone here might see what could be wrong here!
Thanks in advance!
The problem is with your GetHashCode. You should alter it to return the hash code of AllianceName instead.
int IEqualityComparer<Village>.GetHashCode(Village obj)
{
return obj.AllianceName.GetHashCode();
}
The thing is, if Equals returns true, the objects should have the same hash code which is not the case for different Village objects with same AllianceName. Since Distinct works by building a hash table internally, you'll end up with equal objects that won't be matched at all due to different hash codes.
Similarly, to compare two files, if the hash of two files are not the same, you don't need to check the files themselves at all. They will be different. Otherwise, you'll continue to check to see if they are really the same or not. That's exactly what the hash table that Distinct uses behaves.
Or change the line
return alliances.Distinct(new AllianceComparer());
to
return alliances.Select(v => v.AllianceName).Distinct();
I have this function from a plugin (from a previous post)
// This method implements the test condition for
// finding the ResolutionInfo.
private static bool IsResolutionInfo(ImageResource res)
{
return res.ID == (int)ResourceIDs.ResolutionInfo;
}
And the line thats calling this function:
get
{
return (ResolutionInfo)m_imageResources.Find(IsResolutionInfo);
}
So basically I'd like to get rid of the calling function. It's only called twice (once in the get and the other in the set). And It could possible help me to understand inline functions in c#.
get
{
return (ResolutionInfo)m_imageResources.Find(res => res.ID == (int)ResourceIDs.ResolutionInfo);
}
Does that clear it up at all?
Just to further clear things up, looking at reflector, this is what the Find method looks like:
public T Find(Predicate<T> match)
{
if (match == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
for (int i = 0; i < this._size; i++)
{
if (match(this._items[i]))
{
return this._items[i];
}
}
return default(T);
}
So as you can see, it loops through the collection, and for every item in the collection, it passes the item at that index to the Predicate that you passed in (through your lambda). Thus, since we're dealing with generics, it automatically knows the type you're dealing with. It'll be Type T which is whatever type that is in your collection. Makes sense?
Just to add , does the "Find" Function on a list (which is what m_imageresources is) automatically pass the parameter to the IsResoulutionInfo function?
Also, what happens first the cast or the function call?