Ive got the following query that binds to a DropDownList;
if (!Page.IsPostBack)
{
var branchTags =
(
from t in context.ContactSet
orderby t.py3_BranchArea
where t.py3_BranchArea != null
select new
{
BranchTagCode = t.py3_BranchArea,
BranchTag = (t.FormattedValues != null && t.FormattedValues.Contains("py3_brancharea") ? t.FormattedValues["py3_brancharea"] : null)
}
).Distinct();
ddlBranchTags.DataSource = branchTags;
ddlBranchTags.DataBind();
}
For some reason it still ourputs 2 rows that are visually the same. It might be the case that there are two enitites in the CRM with the same name. But, if Im using distinct on the query and only returning the 'py3_brancharea' then surely the Distinct should be run on the actual records returned?
So, this suggests to me -and my limited LINQ knowledge- that its because of the line:
BranchTagCode = t.py3_BranchArea
But, this needs to be called to make it possible to call the FormattedValues.
How then do I get a distinct set of results based purely on 'BranchTag' ?
If Distinct() is not working it is possibly a problem with the particular classes gethashcode() or equals() override methods, which are either not set up correctly or omitted entirely. In a custom class you will most likely need to specify these overrides to get Distinct() and other like methods to function correctly.
You could try to use a where or any clause to differentiate between duplicates as well. Which could be a work around for the Distinct() issues.
To further explain how to set up the Distinct() Method with custom classes. You will need to within the class that you are searching through set the override methods GetHashCode() and Equals(). These or Object level methods that should be in every single class no matter what. To start head to the class in question and type this:
public override bool Equals(object obj)
then
public override int GetHashCode()
Lets say you have this simple class before the overrides:
class foo{
int H {get;set;}
public foo(int _h){
H = _h;
}
}
It would now look like this:
class foo{
int H {get;set;}
int K {get;set;}
public override bool Equals(object obj){
if(obj == null) return false;
foo test = (foo)obj);
if(test == null) return false;
if(this.H == obj.H && this.K == obj.K) return true;
}
public override int GetHashCode(){
int hashH = H.GetHashCode();
int hashK = K.GetHashCode();
return hashH ^ hashK;
}
public foo(int _h){
H = _h;
}
}
Now you could use Distinct() on Ienumerable types containing the foo class like so:
List<foo> FooList = new List<foo>(Collection of 9 Foos);
var res = FooList.Distinct();
Another, much more simple way that worked for me, but may not work in all situations, is using this guys method ( GroupBy() and First()):
Finding Distinct Elements in a List
He creates a List<Customer> customers with FirstName and LastName. Then groups them all by FirstName and grabs the first element from each group!
`
List< Customer > customers = new List< Customer >;
{
new Customer {FirstName = "John", LastName = "Doe"},
new Customer {FirstName = "Jane", LastName = "Doe"},
new Customer {FirstName = "John", LastName = "Doe"},
new Customer {FirstName = "Jay", LastName = null},
new Customer {FirstName = "Jay", LastName = "Doe"}
};
`
Then:
`
var distinctCustomers = customers.GroupBy(s => s.FirstName)
.Select(s => s.First());
`
In my situation, I had to use FirstOrDefault() though.
Is it possible that the two results are different, do they have the same branch tag code and branch tag?
You could implement a custom equality comparer and pass it to distinct() so that it only compares the field that you want? it's a bit more difficult because of the anonymous type in your select statement, but this answer has a way around that.
The default equality comparison for anonymous types is case sensitive. Do the returned values you expect have different casing? As Matt suggested you may want to look at a custom IEqualityComparer implementation on a custom class otherwise.
I changed my code from
.Distinct().ToList();
to
.ToList().Distinct().ToList();
and now it's able to avoid the duplicate. Not sure what's the reason behind.
Another possibility it that the Entity Object has a define key that is not unique.
Related
Is there any way to return a string parameter and a list to the method in c#?
List<cCountry> countries = new List<cCountry>();
countries = GetCountries(userProfile.Country);
private List<cCountry> GetCountries(string p)
{
inputCollection = new cDTOCollection<cDTOBase>();
outputCollection = new cDTOCollection<cDTOBase>();
outputCollection = UpdateProfileBizobj.ProcessRequest(ActionConstants.ActionGetCountriesList, null);
List<cCountry> countries = new List<cCountry>();
cDTOSingleValue SelectedCountryID = new cDTOSingleValue();
foreach (cDTOCountry countryItem in outputCollection)
{
if (p == countryItem.CountryName)
SelectedCountryID.Id = countryItem.CountryID;
countries.Add(Mapper.Map<cDTOCountry, cCountry>(countryItem));
}
countries.Remove(countries[0]);
return countries;
}
Like in the method above I need to return a parameter
SelectedCountryID and also the countries list.Is there any way to
return both?
populate and return an object instead.
public class ReturnObject
{
public List<cCountry> Countries { get; set; }
public guid SelectedCountryID { get; set; } // don't think you defined what type SelectedCountryID is in your question
}
But if you find yourself needing to return more than 1 thing, it's probably an indication that your method is doing too much and should be refactored.
Why can't you reuse the value that you are sending to the method?
return Tuple.Create(new List<Country>(),"astring")
Answer to your question
You can also use the out modifier on a parameter to pass by reference, which would let the method modify the resulting object, effectively returning it. That's a pretty bad design for most cases though, and you probably refactor your methods into smaller pieces or wrap things up into better objects.
In your code:
private List<cCountry> GetCountries(string p, out cDTOSingleValue SelectedCountryID)
{
//your code
}
Potential Refactoring
Looking at your code it, seems your really are trying to do two separate things.
Getting Countries and mapping them
Finding the last country whose name matches the parameter passed in
So as long as your country list isn't ridiculously large, making two separate method calls will make your code more readable and more maintainable. I like using LINQ to manipulate the in-memory collections.
I'd personally use one method to fetch the data.
private List<cDTOCountry> GetCountries()
{
inputCollection = new cDTOCollection<cDTOBase>();
outputCollection = new cDTOCollection<cDTOBase>();
return UpdateProfileBizobj.ProcessRequest(ActionConstants.ActionGetCountriesList, null);
}
And then later I'd process the data as needed:
var preMappedCountries = GetCountries();
var mappedCountries = preMappedCountries
.Select(c => Mapper.Map<cDTOCountry, cCountry>(c)) //map the data
.Skip(1) //skip the first element (the remove index 0)
.ToList(); //cast to List. Read more on LINQ's deferred execution.
var lastMatchingName = preMappedCountries
.LastOrDefault(c => c.Name == p); //gets the last country with matching name
The benefit to separating the logic into pieces is potential reuse of methods. If you ever find yourself needing to get data without mapping, you can do just that and skip all the LINQ logic. This way the logic that gets data is distinct from the logic that matches country names.
In your case an out param seems more appropriate. Otherwise as Kevin suggested you can return a Tuple.
I'm using NUnit 2.5.7. I want to test whether a collection of custom objects of a particular class contains certain objects, based on one of the class's properties.
e.g. a contrived example...
public class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
}
// ...
public List<Person> GetFavouritePeople()
{
List<Person> favouritePeople = new List<Person>();
favouritePeople.Add(new Person("joe"));
favouritePeople.Add(new Person("fred"));
favouritePeople.Add(new Person("jenny"));
return favouritePeople;
}
// ...
[Test]
public GetFavouritePeople()
{
List<Person> people = GetFavouritePeople();
// What I'd like to test, but not sure how to do it...
Assert.Contains(Name="joe", people);
Assert.Contains(Name="fred", people);
Assert.Contains(Name="jenny", people);
}
Although it would be simple enough in this example, I don't want to create mock objects for each Person and use those in the assertion... I just want to check based on a particular property (Name in this example.)
You could use LINQ:
Assert.That(people.Any(p => p.Name == "joe"));
or, if you want to be explicit about there being exactly one person with each name:
Assert.That(people.Count(p => p.Name == "joe"), Is.EqualTo(1));
If you want a better error message than "Assertion failed, expected true, was false", you could create your own assert method.
For several collection-related asserts, CollectionAssert is very useful - for instance, it allows you to check if two collections contain the same elements, irrespective of their order. So yet another possibility is:
CollectionAssert.AreEquivalent(new[] {"joe", "fred", "jenny"}, people.Select(p => p.Name).ToList());
Note that CollectionAssert.AreEquivalent() is a little picky with regard to the types it accepts (even though the signature takes IEnumerable). I usually wrap it in another method that calls ToList() on both parameters before invoking CollectionAssert.AreEquivalent().
You can use Assert.That in conjunction with Has.Exactly(1).Matches:
List<Person> people = GetFavouritePeople()
Assert.That(people, Has.Exactly(1).Matches<Person>(p => p.Name == "NUnit is amazing")));
Failure message will be along lines of:
Expected: exactly one item value matching lambda expression
But was: 0 items < [result of people.ToString()] >
You can use Linq's Intersect() to determine whether all the expected items are within the list your testing against even if the list contains other items that you are not testing for:
[Test]
public void TestFavouritePeople()
{
var people = GetFavouritePeople();
var names = people.Select(p => p.Name);
var expectedNames = new[] {"joe", "fred", "jenny"};
var actualNames = names.Intersect(expectedNames);
CollectionAssert.AreEquivalent(expectedNames, actualNames);
}
For NUnit 3.0 and greater, you can use Is.SupersetOf():
[Test]
public void TestFavouritePeople()
{
var people = GetFavouritePeople();
var names = people.Select(p => p.Name);
var expectedNames = new[] {"joe", "fred", "jienny"};
var actualNames = names;
Assert.That(actualNames, Is.SupersetOf(expectedNames));
}
My main problem description goes like this:
I have two lists of BankAccount objects. A BankAccount has properties such as BankCode and AccountNumber which uniquely identifies an account. So both the lists may contain the same bank account but they may have their Source, Amount, or AccountTypes differing.
The aim here is to merge those two lists:
add accounts to the first list if it is available in the second (but not in the first list).
If the bank accounts are the same in both lists, update the details of the bank account in the first list with the details of the (matching) bank account in the 2nd list.
I've tried implementing the solution mentioned in one SO post. I've went and tried writing my code down at a .NET code pad site. But I am not able to get the output after trying to execute line no. 93 which I've commented.
class BankAccount
{
public string BankCode{get;set;}
public string AccountNumber{get;set;}
public string AccountType{get;set;}
public string Amount{get;set;}
public string Source{get;set;}
public override bool Equals(object obj)
{
var acc = obj as BankAccount;
return Equals(acc);
}
public override int GetHashCode()
{
return this.GetHashCode();
}
public bool Equals(BankAccount acc2)
{
if(acc2 == null) return false;
if(string.IsNullOrEmpty(acc2.BankCode)) return false;
if(string.IsNullOrEmpty(acc2.AccountNumber)) return false;
return this.BankCode.Equals(acc2.BankCode) && this.AccountNumber.Equals(acc2.AccountNumber);
}
}
//List<BankAccount> lst3 = lst.Union(lst1).ToList(); // line 93
Full code can be viewed here.
PS: I'm not sure if this could be a problem with the codepad site or not.
Update - Monday, 14 February 2011 - 4:50:24 (am) / 04:50:24 GMT
Thanx for the update. But something is still amiss. In the output, list 3's first item should have AccountType=P and Source=lst2. The 2nd requirement isn't met. I figure Union() does only a part of what I need. What do I need to do satisfy the 2nd requirement.
EDIT by drachenstern: I'm not sure this title is any better, but it's definitely more informative than the previous title as to the actual question :\
Solution 1:
This solution doesn't achieve your (newly) stated 2 requirements, but aims to fix the issue in your attempt at solving the problem using a LINQ Union().
You've got a recursive call which is causing a stack overflow exception on this line (23):
public override int GetHashCode()
{
return this.GetHashCode();
}
I suggest changing it to something like this:
public override int GetHashCode()
{
return (BankCode + AccountNumber).GetHashCode();
}
EDIT:
Ensure that members BankCount and AccountNumber will never be null or an exception will be thrown. I suggest you look up standard practices for overriding the GetHashCode() method.
Resharper's autogenerated GetHashCode override:
(The 397 value ensures that the numbers won't clash if the BankCode and AccountNumber are swapped. The unchecked means that there won't be overflow issues with the number *397)
public override int GetHashCode()
{
unchecked
{
return ((BankCode != null ? BankCode.GetHashCode() : 0)*397) ^ (AccountNumber != null ? AccountNumber.GetHashCode() : 0);
}
}
Solution 2:
This solution aims to achieve your 2 requirements, without using a LINQ Union().
If you are wanting to merge the lists, using the 2nd list as the preference then perhaps try this:
var mergedList = new List<BankAccount>();
// add items from lst or any duplicates from lst1
foreach (var bankAccount in lst)
{
var account = bankAccount;
var dupe = lst1.FirstOrDefault(item => item.Equals(account));
mergedList.Add(dupe ?? bankAccount);
}
// add any items in lst1 that are not duplicates
foreach (var bankAccount in lst1.Where(item=>!mergedList.Contains(item)))
{
mergedList.Add(bankAccount);
}
If you're looking for code minimization:
// add items from lst or any duplicates from lst1
var temp = lst.Select(item => lst1.FirstOrDefault(item1 => item1.Equals(item)) ?? item);
// add any items in lst1 that are not duplicates
var result = temp.Union(lst1.Where(item => !temp.Contains(item)));
The problem is this method in class BankAccount. It's causing a stack overflow because it keeps calling itself.
public override int GetHashCode()
{
return this.GetHashCode();
}
Try using this.ToString().GetHashCode() and overriding ToString with something that makes sense for the class.
i asked this a few weeks ago, but couldnt get any of the suggested answers working, so i would be grateful for any help on this:
i have a list of event Ids returned from an xml document as shown below
public IEnumerable<EventFeed> GetEventIdsByEventDate(DateTime eventDate)
{
return (from feed in xmlDoc.Descendants("Show")
from ev in feed.Elements("Event")
where Convert.ToDateTime(ev.Attribute("Date").Value).ToShortDateString() == eventDate.ToShortDateString()
select new EventFeed()
{
EventShowCode = feed.Attribute("Code").Value
}).ToList();
}
i now need to query my database to match events that equal the eventIds returned from the above method. so i would have something like:
select * from eventsdb where eventId in GetEventIdsByEventDate()
how can i do this using LINQ
thanks
kb
Hi Prutswonder, ive created the method below based on your suggestion
public IEnumerable<EventFeed> foo(DateTime str)
{
var foo = from f in GetAllEventsFromDatabase().ToList()
where GetAllEventsByDate(str).Contains(f.EventShowCode)
select e;
return (IEnumerable<EventFeed>) foo;
}
but on compile i get the following error
Error 7 The type arguments for method 'System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource>, TSource)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
GetAllEventsFromDatabase:
public IEnumerable<EventFeed> GetAllEventsFromDatabase()
{
var allEvents = from eventsList in GetEventsList()
select new EventFeed()
{
EventName = eventsList.Title,
EventSummary = eventsList.Introduction,
EventShowCode = eventsList.EventId,
EventImageSmall = eventsList.EventImageThumbUrl,
EventUrl = eventsList.Url,
EventSortBy = eventsList.SortOrder
};
return allEvents.OrderBy(x => x.EventSortBy);
}
The GetEventIdsByEventDate() method should return an IEnumerable of strings, containing the Event Ids (like the method name implies):
public IEnumerable<string> GetEventIdsByEventDate(DateTime eventDate)
{
return (from feed in xmlDoc.Descendants("Show")
from ev in feed.Elements("Event")
where Convert.ToDateTime(ev.Attribute("Date").Value).ToShortDateString() == eventDate.ToShortDateString()
select feed.Attribute("Code").Value
).ToList();
}
Also, don't forget to rename the foo() method to a more suitable name (for example GetEventsByEventDate())
About your error:
GetAllEventsByDate returns an IEnumerable containing EventFeed objects, so when you use the "Contains" method, it expects an "EventFeed" object to compare to the objects in the list. Instead, you are passing it an f.EventShowCode, which I assume is an integer or something:
EventShowCode = eventsList.EventId
I believe what you're looking for is this:
public IEnumerable<EventFeed> foo(DateTime str)
{
var foo = from f in GetAllEventsFromDatabase()
where GetAllEventsByDate(str).Contains(f)
select f;
return foo;
}
Download LINQPad. It's free but the upgraded version provides Intellisense support. This app has helped me figure out some pretty complicated LINQ queries.
I've got 2 lists of the same object. Now the first list is filled with data from a database table. the other list is filled with data downloaded a server.
Example:
public class HistoricData {
public int Id{get;set;}
public DateTime Date {get;set;}
public string Name {get;set;}
public float Impressions {get;set;}
}
So in my object I have the Id property that is unique. Now I need to check if I have objects in my second list that aren't in my first list.
I thought that I could do it in Linq, but I'm a little stuck.
var difference = from objHD in objHistoricData
join objHDN in objHistoricDataNew on objHD.Id equals objHDN.Id
select new {objHDNA = objHDN};
This always returns 0.
var difference = objHistoricDataNew.Except(objHistoricDataNew, new HistoricDataComparer());
(where HistoricDataComparer is a IEqualityComparer<HistoricData>)
use the Except function from LINQ, ans pass the IEqualityComparer that compare the ID.
var difference = a.Except( b, new YouEqualitityComparer() )
You need to override Equals and GetHashcode so that your types are compared by their values rather then by their references.
Try
var difference = list1.Except(list2);
But I think you'll need your own GetHashCode and Equals methods.
I assume that you would like to select all items in objHistoricDataNew that are different, than stored in objHistoricData?
Then maybe something like that:
var difference = objhistoricDataNew.Where(objHDN => objHistoricData.Select(objHD => objHD.Id).IndexOf(objHDN.Id) == -1)