Check if array contains property id with Linq / C# - c#

currently I loop through arrays and check if any objects contain a specific id. These objects have a Id property.
public class MyObj
{
public int Id {get; set;}
}
So when checking the locked state I go for this code
bool IsUnlocked(int targetId) {
bool isUnlocked = false;
for (int i = 0; i < myObjs.Length; i++) // loop trough the objects
{
MyObj current = myObjs[i];
if (current.Id == targetId) // a match
{
isUnlocked = true;
break;
}
}
return isUnlocked;
}
I think this can be done smarter with Linq. I tried
bool isUnlocked = myObjs.Contains(current => current.Id == targetId);
but this is a wrong syntax. Do I have to setup something like
myObjs.First(current => current.Id == targetId);

Contains doesn't take a delegate type so passing the behaviour of current => current.Id == targetId into it would not compile.
As for myObjs.First(current => current.Id == targetId);, this will return the first object that satisfies the provided predicate as opposed to returning a bool indicated if there is any item that satisfies the provided predicate or not.
The solution is to use the Any extension method.
bool isUnlocked = myObjs.Any(current => current.Id == targetId);

There is also a dedicated method in the Array class - Array.Exists:
isUnlocked = Array.Exists(myObjs, elem => elem.Id == targetId);

Related

What to do to get only one List?

Hello i have a method that compares the objects of 2 Lists for differences. Right now this works but only for one property at a time.
Here is the Method:
public SPpowerPlantList compareTwoLists(string sqlServer, string database, DateTime timestampCurrent, string noteCurrent, DateTime timestampOld, string noteOld)
{
int count = 0;
SPpowerPlantList powerPlantListCurrent = loadProjectsAndComponentsFromSqlServer(sqlServer, database, timestampCurrent, noteCurrent);
SPpowerPlantList powerPlantListOld = loadProjectsAndComponentsFromSqlServer(sqlServer, database, timestampOld, noteOld);
SPpowerPlantList powerPlantListDifferences = new SPpowerPlantList();
count = powerPlantListOld.Count - powerPlantListCurrent.Count;
var differentObjects = powerPlantListCurrent.Where(p => !powerPlantListOld.Any(l => p.mwWeb == l.mwWeb)).ToList();
foreach (var differentObject in differentObjects)
{
powerPlantListDifferences.Add(differentObject);
}
return powerPlantListDifferences;
}
This works and i get 4 Objects in the new List. The Problem is that i have a few other properties that i need to compare. Instead of mwWeb for example name. When i try to change it i need for every new property a new List and a new Foreach-Loop.
e.g.
int count = 0;
SPpowerPlantList powerPlantListCurrent = loadProjectsAndComponentsFromSqlServer(sqlServer, database, timestampCurrent, noteCurrent);
SPpowerPlantList powerPlantListOld = loadProjectsAndComponentsFromSqlServer(sqlServer, database, timestampOld, noteOld);
SPpowerPlantList powerPlantListDifferences = new SPpowerPlantList();
SPpowerPlantList powerPlantListDifferences2 = new SPpowerPlantList();
count = powerPlantListOld.Count - powerPlantListCurrent.Count;
var differentObjects = powerPlantListCurrent.Where(p => !powerPlantListOld.Any(l => p.mwWeb == l.mwWeb)).ToList();
var differentObjects2 = powerPlantListCurrent.Where(p => !powerPlantListOld.Any(l => p.shortName == l.shortName)).ToList();
foreach (var differentObject in differentObjects)
{
powerPlantListDifferences.Add(differentObject);
}
foreach (var differentObject in differentObjects2)
{
powerPlantListDifferences2.Add(differentObject);
}
return powerPlantListDifferences;
Is there a way to prevent this? or to make more querys and get only 1 List with all different Objects back?
I tried it with except and intersect but that didnt worked.
So any help or advise would be great and thx for your time.
PS: If there is something wrong with my question-style please say it to me becouse i try to learn to ask better questions.
You may be able to simply chain the properties that you wanted to compare within your Where() clause using OR statements :
// This should get you any elements that have different A properties, B properties, etc.
var different = current.Where(p => !old.Any(l => p.A == l.A || p.B == l.B))
.ToList();
If that doesn't work and you really want to use the Except() or Intersect() methods to properly compare the objects, you could write your own custom IEqualityComparer<YourPowerPlant> to use to properly compare them :
class PowerPlantComparer : IEqualityComparer<YourPowerPlant>
{
// Powerplants are are equal if specific properties are equal.
public bool Equals(YourPowerPlant x, YourPowerPlant 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;
// Checks the other properties to compare (examples using mwWeb and shortName)
return x.mwWeb == y.mwWeb && x.shortName == y.shortName;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(YourPowerPlant powerPlant)
{
// Check whether the object is null
if (Object.ReferenceEquals(powerPlant, null)) return 0;
// Get hash code for the mwWeb field if it is not null.
int hashA = powerPlant.mwWeb == null ? 0 : powerPlant.mwWeb.GetHashCode();
// Get hash code for the shortName field if it is not null.
int hashB = powerPlant.shortName == null ? 0 : powerPlant.shortName.GetHashCode();
// Calculate the hash code for the product.
return hashA ^ hashB;
}
}
and then you could likely use something like one of the following depending on your needs :
var different = current.Except(old,new PowerPlantComparer());
or :
var different = current.Intersect(old,new PowerPlantComparer());
One way is to use IEqualityComparer as Rion Williams suggested, if you'd like a more flexible solution you can split logic in to two parts. First create helper method that accepts two lists, and function where you can define what properties you wish to compare. For example :
public static class Helper
{
public static SPpowerPlantList GetDifference(this SPpowerPlantList current, SPpowerPlantList old, Func<PowerPlant, PowerPlant, bool> func)
{
var diff = current.Where(p => old.All(l => func(p, l))).ToList();
var result = new SPpowerPlantList();
foreach (var item in diff) result.Add(item);
return result;
}
}
And use it :
public SPpowerPlantList compareTwoLists(string sqlServer, string database,
DateTime timestampCurrent, string noteCurrent,
DateTime timestampOld, string noteOld)
{
var powerPlantListCurrent = ...;
var powerPlantListOld = ...;
var diff = powerPlantListCurrent.GetDifference(
powerPlantListOld,
(x, y) => x.mwWeb != y.mwWeb ||
x.shortName != y.shortName);
return diff;
}
P.S. if it better suits your needs, you could move method inside of existing class :
public class MyClass
{
public SPpowerPlantList GetDifference(SPpowerPlantList current, SPpowerPlantList old, Func<PowerPlant, PowerPlant, bool> func)
{
...
}
}
And call it (inside of class) :
var result = GetDifference(currentValues, oldValues, (x, y) => x.mwWeb != y.mwWeb);
The easiest way to do this would be to compare some unique identifier (ID)
var differentObjects = powerPlantListCurrent
.Where(p => !powerPlantListOld.Any(l => p.Id == l.Id)
.ToList();
If the other properties might have been updated and you want to check that too, you'll have to compare all of them to detect changes made to existing elements:
Implement a camparison-method (IComparable, IEquatable, IEqualityComparer, or override Equals) or, if that's not possible because you didn't write the class yourself (code generated or external assembly), write a method to compare two of those SPpowerPlantList elements and use that instead of comparing every single property in Linq. For example:
public bool AreThoseTheSame(SPpowerPlantList a,SPpowerPlantList b)
{
if(a.mwWeb != b.mwWeb) return false;
if(a.shortName != b.shortName) return false;
//etc.
return true;
}
Then replace your difference call with this:
var differentObjects = powerPlantListCurrent
.Where(p => !powerPlantListOld.Any(l => AreThoseTheSame(p,l))
.ToList();

How can I break a `RemoveAll()`statement in C#

I'm using the RemoveAll() statement, that do a foreach element of the list, and based in a condition returned by the delegate, it removes or not the element from the list. Like this:
x.RemoveAll(delegate(string y)
{
if (y == "abc")
return true;
return false;
});
I want to break the foreach from the removeAll, so that upon fulfilling some condition, I no longer even try to remove elements. Something like this:
x.RemoveAll(delegate(string y)
{
if (Foo() || Bar())
break; //stop trying to remove elements
if (y == "abc")
return true;
return false;
});
Have a way to do this without a auxiliary variable?
P.S: Using a auxiliary variable I know how to do.
There are two real options. A variable telling you not to remove more items:
var done = false;
list.RemoveAll(item =>
{
if(done) return false;
if(Foo() || Bar())
{
done = true;
return false;
}
return item == "abc";
}
Or throwing an exception (despite the fact that it's really poor practice to use exceptions for control flow).
list.RemoveAll(item =>
{
if(Foo() || Bar())
throw new SomeTypeOfException()
return item == "abc";
}
If Foo or Bar being true really are exceptional/error cases, then maybe you could justify it, but it certainly seems like code smell. Note that this is technically going to be the only way to use RemoveAll and not actually invoke the delegate on any later items.
Fundamentally the problem is that the operation you're trying to perform isn't in line with what RemoveAll was designed to do. What you really want is a version of the method that supports cancellation, or sufficient access to the internals of the list to create a comparable method with the appropriate cancellation. Sadly, you don't have access to the underlying array in order to be able to replicate the ability of RemoveAll to remove multiple items without moving up all of the items until the very end, unless you re-create your own entire list based structure.
Using simple loop with break will be way more efficient then using DeleteAll with trigger to generate false for all elements after a certain point.
Why not rather filter first:
foreach(var item in x.Where(o => Foo(o)).ToList())
x.Remove(item);
If you care about efficiency, then
for(int i = 0; i++; i < x.Length)
if(Foo(x[i]))
{
x.RemoveAt(i);
break; // if list is sorted
}
else
i++;
For unsorted list it's more optimal to go from top to bottom afaik.
You could do this with an extension method:
public static IEnumerable<T> RemoveAllUntil<T>(
this IEnumerable<T> input,
Predicate<T> match,
Predicate<T> until)
{
bool untilFound = false;
foreach (T element in input)
{
if(!untilFound) untilFound = until(element);
if(untilFound || !match(element))
{
yield return element;
}
}
}
And use it like this:
var strings = new List<string> { "s1", "s2", "s2", "break", "s2", "s3"};
strings = strings.RemoveAllUntil(
s => s == "s2",
s => s == "break")
.ToList();
This will give you:
s1, break, s2, s3
Long comment: approximate code for removing item with cancellation and avoiding multiple copying of the tail:
void RemoveWithCancelation(this List<T> list,
Func<RemoveWithCancelationResult> predicate)
{
var indexToKeep = -1;
for (var i = 0; i < list.Count; i++)
{
var condition = predicate(list[i]);
if (condition.Cancel)
break;
if (!condition.RemoveItem)
{
indexToKeep++;
list[indexToKeep] = list[i];
}
}
if (indexToKeep+1 < list.Count)
list.RemoveRange(indexToKeep+1, list.Count);
}
I don't think this can be done in the RemoveAll().
However, you could 'simulate' the break with TakeWhile() and then filter the list with Where()
var newList = x.TakeWhile(elem => elem != "your condition").Where(elem => elem == "abc");
You could take advantage of closures:
bool stop = false; // the delegate closes over 'stop'
x.RemoveAll(delegate(string y)
{
if (!stop && y == "abc")
return true;
if (Foo() || Bar())
stop = true;
return false;
});

Using Entity Framework, which method is more efficient?

I have some code that changes a value of some data within my database while within a loop. I'm just wondering what is the most efficient way of filtering my data first? I'll give an example:-
With the class:-
public class myObj
{
int id {get;set;}
string product {get; set;}
string parent{get;set;}
bool received {get;set;}
}
And the DbContext:-
public class myCont:DbContext
{
public DbSet<myObj> myObjs {get;set;}
}
Is it better to do this:-
int[] list;
/* Populate list with a bunch of id numbers found in myOBjs */
myCont data = new myCont();
myObj ob = data.myObjs.Where(o => o.parent == "number1");
foreach(int i in list)
{
ob.First(o => o.id == i && o.received != true).received = true;
}
Or:-
int[] list;
/* Populate list with a bunch of id numbers found in myOBjs */
myCont data = new myCont();
foreach(int i in list)
{
data.myObjs.First(o => o.parent == "number1" && o.id == i && o.received != true).received = true;
}
Or is there no difference?
Not sure how you get to compile your code example above.
In your myObj object, the received property is an int, yet you are evaluating it against a bool which should cause this line o.received != true to results in an error Cannot apply operator '!=' to operands of type 'int' and 'bool'.
To Check the SQL
Once the code compiles use SQL Profiler to see what SQL is generated.
That will show you the constructed SQLs
Benchmarking
The below is a very crude description of only one possible way you can benchmark your code execution.
Wrap your code into a method, for example:
public void TestingOperationOneWay()
{
int[] list;
/* Populate list with a bunch of id numbers found in myOBjs */
myCont data = new myCont();
myObj ob = data.myObjs.Where(o => o.parent == "number1");
foreach(int i in list)
{
ob.First(o => o.id == i && o.received != true).received = true;
}
}
And:
public void TestingOperationAnotherWay()
{
int[] list;
/* Populate list with a bunch of id numbers found in myOBjs */
myCont data = new myCont();
foreach(int i in list)
{
data.myObjs.First(o => o.parent == "number1" && o.id == i && o.received != true).received = true;
}
}
Crate a method which iterates x amount of times over each method using the Stopwatch similar to this:
private static TimeSpan ExecuteOneWayTest(int iterations)
{
var stopwatch = Stopwatch.StartNew();
for (var i = 1; i < iterations; i++)
{
TestingOperationOneWay();
}
stopwatch.Stop();
return stopwatch.Elapsed;
}
Evaluate the results similar to this:
static void RunTests()
{
const int iterations = 100000000;
var timespanRun1 = ExecuteOneWayTest(iterations);
var timespanRun2 = ExecuteAnotherWayTest(iterations);
// Evaluate Results....
}
In the case of a choice between your two queries, I agree that they would both execute similarly, and benchmarking is an appropriate response. However, there are some things you can do to optimize. For example, you could use the method 'AsEnumerable' to force evaluation using the IEnumerable 'Where' vice the LINQ 'Where' clause (a difference of translating into SQL and executing against the data source or handling the where within the object hierarchy). Since you appear to be manipulating only properties (and not Entity Relationships), you could do this:
int[] list;
/* Populate list with a bunch of id numbers found in myOBjs */
myCont data = new myCont();
myObj ob = data.myObjs.Where(o => o.parent == "number1").AsEnumerable<myObj>();
foreach(int i in list)
{
ob.First(o => o.id == i && o.received != true).received = true;
}
Doing so would avoid the penalty of hitting the database for each record (possibly avoiding network latency), but would increase your memory footprint. Here's an associated LINQ further explaining this idea. It really depends on where you can absorb the performance cost.

Filtering a c# collection by multiple parameters where some may be null

I'm sorry if this is a duplicate question, I have found some similar ones, but not one that would solve my problem. I have a collection of objects with various parameters and I want to filter them using data from ComboBoxes and TextBoxes.
var query = from zaj in zajezdy
where zaj.Zeme == (String)zemeCombo.SelectedValue
&& zaj.Oblast == (String)oblastCombo.SelectedValue
&& zaj.Stredisko == (String)strediskoCombo.SelectedValue
&& zaj.Doprava.Contains((String)dopravaCombo.SelectedValue)
&& zaj.Strava.Contains((String)stravaCombo.SelectedValue)
&& zaj.CenaOd > Int32.Parse(cenaOdText.Text)
&& zaj.CenaOd < Int32.Parse(cenaDoText.Text)
select zaj;
This code works if all the combos have been properly selected. However, if the user leaves some unselected/empty, the query comes back with zero objects in it. How can I detect which parameters are null so that the query ignores them?
I think this is a nice example for Specification usage.
Create object, which will represent zajezd specification:
public interface ISpecification<T>
{
bool IsSatisfiedBy(T value);
}
public class ZajezdSpecification : ISpecification<Zajezd>
{
private string _zeme;
private string _oblast;
private string _stredisko;
private string _doprava;
private string _strava;
private int _cenaOd;
private int _cenaDo;
public ZajezdSpecification(string zeme, string oblast, string stredisko,
string doprava, string strava, int cenaOd, int cenaDo)
{
_zeme = zeme;
_oblast = oblast;
_stredisko = stredisko;
_doprava = doprava;
_strava = strava;
_cenaOd = cenaOd;
_cenaDo = cenaDo;
}
public bool IsSatisfiedBy(Zajezd zajezd)
{
if (!String.IsNullOrEmpty(_zeme) && zajezd.Zeme != _zeme)
return false;
if (!String.IsNullOrEmpty(_oblast) && zajezd.Oblast != _oblast)
return false;
// ... verify anything you want
return _cenaOd < zajezd.CenaOd && zajezd.CenaOd < _cenaDo;
}
}
and initialize it with values from UI:
ZajezdSpecification spec = new ZajezdSpecification(
(string)zemeCombo.SelectedValue,
(string)oblastCombo.SelectedValue,
(string)strediskoCombo.SelectedValue,
...
Int32.Parse(cenaDoText.Text)
);
Use this specification to filter your collection:
var query = from zaj in zajezdy
where spec.IsSatisfiedBy(zaj)
select zaj;
PS try to use English names in your code.
First of all, try to introduse separate variables for each combo\text and use it. Use String.IsNullOrEmpty to check strings. Also try to avoid Type.Parse() - use Type.TryParse() instead. Sample :
var query = from c in dc.Customers select c;
//filter the result set based on user inputs
if ( !string.IsNullOrEmpty( countryFilter ) )
query = query.Where ( c=>c.Country == countryFilter );
if ( !string.IsNullOrEmpty( cityFilter ) )
query = query.Where ( c=>c.City == cityFilter );
You could also filter them like this, depending on whether the parameter name is null then all items are returned, otherwise only the elements with the specified name are returned:
myList.Where(x => name == null || x.Name == name)
.Where(x => id == null || x.Id == id);

Excluding items from a list by object property

I'm trying to build a list of items based on their presence in a list.
itemsAll contains all products
itemsNew contains only new products
I'd like itemsOld to contain only old products (i.e. itemsAll -
itemsNew)
This was my approach, which doesn't return the correct number of items.
var itemsAll = objProductStagingRepository.AllImports(fileId, cid).ToList();
var itemsNew = objProductStagingRepository.DetectNonPresentProductNames(fileId, cid).ToList();
var itemsOld = from t1 in itemsAll where !(from o in itemsNew select o.Id).Contains(t1.Id)
select t1; // this does not work
Does anybody have any suggestions as to how I shuold be approacing this? I have tried itemsAll.Except(itemsNew) which also doesn't yield the correct results!
I think you probably could use the Except method, but you would need to provide an equality comparer for the method to know when two items are equal.
http://msdn.microsoft.com/en-us/library/bb336390.aspx
In your question it looks like you're not using your own comparer, so it's comparing the items to see if they are the same object in memory (most likely), which is not what you're trying to do.
You want to compare the objects by database identity, which means you need to provide you're own comparer.
Example:
public class Item
{
public int Id { get; set; }
}
class ItemComparer : IEqualityComparer<Item>
{
public bool Equals(Item x, Item y)
{
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.Id == y.Id;
}
public int GetHashCode(Item value)
{
if (Object.ReferenceEquals(value, null)) return 0;
int hash = value.Id.GetHashCode();
return hash;
}
}
itemsOld.AddRange(itemsAll.Where(p => !itemsNew.Any(a => a.Id == p.Id)));
I prefer the fluent syntax so:
var itemsOld = itemsAll.Where(x => !itemsNew.Any(y => y.Id == x.Id));
or
var itemsOld = itemsAll.Where(x => !itemsNew.Exists(y => y.Id == x.Id));
This might work
var itemsOld = from a in itemsAll
join n in itemsNew on a.Id equals n.Id into ng
where !ng.Any()
select a;

Categories