protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
string Card = (Request.Params["Card"]);
DateTime Date = DateTime.Parse(Request.Params["Date"]);
using ( AttendanceContext db = new AttendanceContext())
{
lblEmpName.Text = db.users.Where(t => t.Card == Card).SingleOrDefault().EmployeeName;
lblDate.Text = Date.ToString("dd/MM/yyyy");
if (lblEmpName.Text == null)
{
lblEmpName.Text = "No Data";
}
if (lblDate.Text == null)
{
lblDate.Text = "No Date";
}
var firstArray = db.TimeoutJustification.Where(x => x.Date == Date && x.Card == Card && x.GeneralJustification != null).ToList();
var SecondArray = GetTimeOutData(Card, Date).OrderByDescending(t => t.Date).ToList();
///This is where i need to check both arrays
var filtered = firstArray.Except(SecondArray);
//var d = newTimeoutdata.Where(t => !newTimeoutdata.Contains(t.TimeOut.ToString())).ToList();
if(filtered!=null){
//This is where i will insert the unmatched array
db.TimeoutJustification.AddRange(filtered);
db.SaveChanges();
}
}
}
}
}
My first array contains 3 items that are identical to 3 of the items in my second array.
I need to get the 4th unmatched item from my second array only. keep in mind that I need to compare both arrays a using datetime TimeOut since date and card will always be the same in all arrays
You can use the Except method, something like this:
secondArray.Except(firstArray)
However you might also need to use a custom equality comparer for this purpose. I am not sure about your properties inside your class, you can do something like this for instance:
public class DistinctItemComparer : IEqualityComparer<yourClass>
{
public bool Equals(yourClass x, yourClass y)
{
return x.Id == y.Id &&
x.Date == y.Date &&
x.Card == y.Card;
}
public int GetHashCode(yourClass obj)
{
return obj.Id.GetHashCode() ^
obj.Date.GetHashCode() ^
obj.Card.GetHashCode();
}
}
And then using it like this:
secondArray.Except(firstArray, new DistinctItemComparer())
Related
I have a custom class called PairString
public class PairString: IComparer<PairString>
{
public string first;
public string second;
public PairString(string f, string s)
{
first = f;
second = s;
}
public int Compare([AllowNull] PairString x, [AllowNull] PairString y)
{
if (x == null || y == null) return -1;
var f = string.Compare(x.first, y.first);
var s = string.Compare(x.second, y.second);
return f == s ? s : f;
}
}
I want to create groups by count and then by lexical order of strings in that groups, from a list of input PairString List. Below method does the grouping right. But when I try to sort the groups in lexical order for equal count groups, it throws "Atleast one object must implement IComparer error"
public static List<string> MaxItemAssociatoinGroup(List<PairString> input)
{
if (input == null || input.Count == 0) return null;
List<SortedSet<string>> output = new List<SortedSet<string>>();
foreach (var item in input)
{
if (output.Any(x => x.Contains(item.first) || x.Contains(item.second)))
{
//Take the set containing one or two or both items
var set1 = output.FirstOrDefault(x => x.Contains(item.first));
var set2 = output.FirstOrDefault(x => x.Contains(item.second));
if (set1 == null)
set2.UnionWith(new SortedSet<string> { item.first, item.second });
else if (set2 == null)
set1.UnionWith(new SortedSet<string> { item.first, item.second });
else if (set1 != set2)
{
set1.UnionWith(set2);
output.Remove(set2);
}
}
else
output.Add(new SortedSet<string>(new List<string>() { item.first, item.second }));
}
var maxlistAssociation = output.OrderByDescending(x => x.Count).First();
return new List<string>(maxlistAssociation);
}
I am not sure how to achieve lexical order for same count groups,
Sample input is
new PairString("item3","item4"),
new PairString("item3","item6"),
new PairString("item5","item6"),
new PairString("item2","item8"),
new PairString("item8","item9"),
new PairString("item1","item2")
it groups into 2 groups of equal count {item3,item4,item5,item6} & {item1,item2,item8,item9} but returns {item3,item4,item5,item6} as its first in the list. but I want the second group as it contains the item that lexicographically first than first group. what am I missing here?
It appears that you're missing a method that will compare two SortedSet<string> objects and return the one which comes first lexically. One way to do this is to compare each item from one set with the corresponding one in the other set, and return the first non-equal comparison:
public class SortedSetComparer<T> : IComparer<SortedSet<T>> where T : IComparable<T>
{
public int Compare(SortedSet<T> x, SortedSet<T> y)
{
// Null checks
if (x == null) return y == null ? 0 : 1;
if (y == null) return -1;
var minCount = Math.Min(x.Count, y.Count);
// Compare each item from one set with the corresponding one in the other set
for (var i = 0; i < minCount; i++)
{
var result = x.ElementAt(i).CompareTo(y.ElementAt(i));
// Return the first non-equal result
if (result != 0) return result;
}
// If all the items were equal, return the comparison of the Count
return x.Count.CompareTo(y.Count);
}
}
Then we can order our results (after sorting by size) by passing an instance of this class to the ThenBy method:
var maxlistAssociation = output
.OrderByDescending(x => x.Count)
.ThenBy(x => x, new SortedSetComparer<string>())
.First();
Depending on the behavior you want from this method, we could also incorporate the ordering by Count into our comparison method, so that it puts the sets with the most items first, then sorts them alphabetically:
public class SortedSetComparer<T> : IComparer<SortedSet<T>> where T : IComparable<T>
{
public int Compare(SortedSet<T> x, SortedSet<T> y)
{
// Null checks
if (x == null) return y == null ? 0 : 1;
if (y == null) return -1;
// Compare the counts first, in descending order
var countComparison = x.Count.CompareTo(y.Count);
if (countComparison != 0) return countComparison * -1;
// Then compare each item from one set lecially
// with the corresponding one in the other set
return x.Select((item, index) =>
x.ElementAt(index).CompareTo(y.ElementAt(index)))
.FirstOrDefault(result => result != 0);
}
}
And now we only need one OrderBy clause:
var maxlistAssociation = output
.OrderBy(x => x, new SortedSetComparer<string>())
.First();
I am trying to find out the duplicate Elements in XElement , and make a generic function to remove duplicates .Something like:
public List<Xelement>RemoveDuplicatesFromXml(List<Xelement> xele)
{ // pass the Xelement List in the Argument and get the List back , after deleting the duplicate entries.
return xele;
}
the xml is as follows:
<Execute ID="7300" Attrib1="xyz" Attrib2="abc" Attrib3="mno" Attrib4="pqr" Attrib5="BCD" />
<Execute ID="7301" Attrib1="xyz" Attrib2="abc" Attrib3="mno" Attrib4="pqr" Attrib5="BCD" />
<Execute ID="7302" Attrib1="xyz1" Attrib2="abc" Attrib3="mno" Attrib4="pqr" Attrib5="BCD" />
I want get duplicates on every attribute excluding ID ,and then delete the one having lesser ID.
Thanks,
You can implement custom IEqualityComparer for this task
class XComparer : IEqualityComparer<XElement>
{
public IList<string> _exceptions;
public XComparer(params string[] exceptions)
{
_exceptions = new List<string>(exceptions);
}
public bool Equals(XElement a, XElement b)
{
var attA = a.Attributes().ToList();
var attB = b.Attributes().ToList();
var setA = AttributeNames(attA);
var setB = AttributeNames(attB);
if (!setA.SetEquals(setB))
{
return false;
}
foreach (var e in setA)
{
var xa = attA.First(x => x.Name.LocalName == e);
var xb = attB.First(x => x.Name.LocalName == e);
if (xa.Value == null && xb.Value == null)
continue;
if (xa.Value == null || xb.Value == null)
return false;
if (!xa.Value.Equals(xb.Value))
{
return false;
}
}
return true;
}
private HashSet<string> AttributeNames(IList<XAttribute> e)
{
return new HashSet<string>(e.Select(x =>x.Name.LocalName).Except(_exceptions));
}
public int GetHashCode(XElement e)
{
var h = 0;
var atts = e.Attributes().ToList();
var names = AttributeNames(atts);
foreach (var a in names)
{
var xa = atts.First(x => x.Name.LocalName == a);
if (xa.Value != null)
{
h = h ^ xa.Value.GetHashCode();
}
}
return h;
}
}
Usage:
var comp = new XComparer("ID");
var distXEle = xele.Distinct(comp);
Please note that IEqualityComparer implementation in this answer only compare LocalName and doesn't take namespace into considerataion. If you have element with duplicate local name attribute, then this implementation will take the first one.
You can see the demo here : https://dotnetfiddle.net/w2DteS
Edit
If you want to
delete the one having lesser ID
It means you want the largest ID, then you can chain the .Distinct call with .Select.
var comp = new XComparer("ID");
var distXEle = xele
.Distinct(comp)
.Select(z => xele
.Where(a => comp.Equals(z, a))
.OrderByDescending(a => int.Parse(a.Attribute("ID").Value))
.First()
);
It will guarantee that you get the element with largest ID.
Use Linq GroupBy
var doc = XDocument.Parse(yourXmlString);
var groups = doc.Root
.Elements()
.GroupBy(element => new
{
Attrib1 = element.Attribute("Attrib1").Value,
Attrib2 = element.Attribute("Attrib2").Value,
Attrib3 = element.Attribute("Attrib3").Value,
Attrib4 = element.Attribute("Attrib4").Value,
Attrib5 = element.Attribute("Attrib5").Value
});
var duplicates = group1.SelectMany(group =>
{
if(group.Count() == 1) // remove this if you want only duplicates
{
return group;
}
int minId = group.Min(element => int.Parse(element.Attribute("ID").Value));
return group.Where(element => int.Parse(element.Attribute("ID").Value) > minId);
});
Solution above will remove elements with lesser ID which have duplicates by attributes.
If you want return only elements which have duplicates then remove if fork from last lambda
I'm working on a course listing in C# and an course can have up to 5 dates of when they are running. Ideally, the next date after today in the future would be selected, and ordered accordingly in a list.
What i have so far is a course list that gets the next date, and displays it, but it displays all the events without dates first (Null/Blank). I'm trying to show the courses with next dates first, and then those without after this.
C# Code:
public ActionResult FilterList(string role = null, string category = null)
{
return View("~/Views/FilterList.cshtml", GetCourses(role, category));
}
[NonAction]
public List<IEnumerable<Course>> GetCourses(string role = null, string category = null)
{
var collection = new List<IEnumerable<Course>>();
var items = Sitecore.Context.Database.GetItem(SitecoreIDs.Pages.CourseRoot)
.Children.Where(m => m.TemplateID == Course.TemplateID)
.Select(m => (Course)m).ToList();
var dates = new List<FilterDates>();
items.ForEach(m => dates.Add(new FilterDates
{
Dates = new List<DateTime>{ m.Date1, m.Date2, m.Date3, m.Date4, m.Date5 },
Name = m.Name
}));
dates.ForEach(m => m.Dates.RemoveAll(n => n == new DateTime(0001, 01, 01)));
dates.ForEach(m => m.Dates.Sort((a, b) => a.CompareTo(b)));
dates = dates.OrderBy(m => m.Dates.AsQueryable().FirstOrDefault(n => n - DateTime.Now >= TimeSpan.Zero)).ToList();
var model = new List<Course>();
dates.ForEach(m => model.Add(items.AsQueryable().FirstOrDefault(n => n.Name == m.Name)));
if (!string.IsNullOrEmpty(role) || !string.IsNullOrEmpty(category))
{
var currentRole = Sitecore.Context.Database.GetItem(SitecoreIDs.Pages.CategoryRoot)
.Children.AsQueryable().FirstOrDefault(m => m.Fields["Key"].Value == role);
if (!string.IsNullOrEmpty(category))
{
var currentCategory = Sitecore.Context.Database.GetItem(SitecoreIDs.Pages.SeriesRoot)
.Children.AsQueryable().FirstOrDefault(m => m.Fields["Key"].Value == category);
model = model.Where(m => m.Series == currentCategory.Name).ToList();
if (string.IsNullOrEmpty(role))
{
collection.Add(model);
}
}
if (!string.IsNullOrEmpty(role))
{
model = model.Where(m => m.InnerItem.Children.Where(n => n.Fields["Key"].Value == currentRole.Name).Any()).ToList();
List<Course> required = new List<Course>(), recommended = new List<Course>(), refresh = new List<Course>();
foreach (var item in model)
{
foreach (Item inner in item.InnerItem.Children)
{
if (inner.Fields["Key"].Value == currentRole.Name)
{
switch (inner.Fields["Severity"].Value)
{
case "Required":
required.Add(item);
break;
case "Recommended":
recommended.Add(item);
break;
case "Refresh":
refresh.Add(item);
break;
}
}
}
}
collection.Add(required);
collection.Add(recommended);
collection.Add(refresh);
}
}
else
{
collection.Add(model);
}
return collection;
}
I've tried different orderbys, but can't seem to get the ordering right. Any help would be greatly appreciated.
Andy
The code you posted has some extra stuff that seems unrelated to your question about sorting. I am ignoring that and just addressing the question at hand: how to sort your courses so that the ones with the nearest future date are first.
I would create a little method to return the next future date or DateTime.MaxValue as the "null" value.
private DateTime GetNextFutureDate(Course course)
{
var dates =
new[] {course.Date1, course.Date2, course.Date3, course.Date4, course.Date5}.Where(d => d > DateTime.Now).ToArray();
return dates.Length == 0 ? DateTime.MaxValue : dates[0];
}
Then in your GetCourses method you could use it like this:
[NonAction]
public List<IEnumerable<Course>> GetCourses(string role = null, string category = null)
{
var collection = new List<IEnumerable<Course>>();
var model = Sitecore.Context.Database.GetItem(SitecoreIDs.Pages.CourseRoot)
.Children.Where(m => m.TemplateID == Course.TemplateID)
.Select(m => (Course)m).OrderBy(m => GetNextFutureDate(m));
if (!string.IsNullOrEmpty(role) || !string.IsNullOrEmpty(category))
// ... the rest of your code ...
return collection;
}
You might also want to consider making GetNextFutureDate a member or extension method on your Course class.
I know this question looks like a duplicate but it seems like each case has it's own solution and I can't find the right explanation for this Exception:
LINQ to Entities does not recognize the method 'Boolean
DateReservationOnTheSameWeek(System.DateTime)' method, and this method
cannot be translated into a store expression.
I just want to compare if two dates are on the same iso week of year, here's my code:
public class Reservation
{
public DateTime DateReservation { get; set; }
// other methods and properties
public static bool IsOnQuota(Reservation item)
{
using (var ctx = new SchedulingToolContext())
{
float qteSem = ctx.Reservations.Where(k => k.CltNameAlpha == item.CltNameAlpha
&& k.DateReservation.Year == item.DateReservation.Year
&& item.DateReservationOnTheSameWeek(k.DateReservation) // this causes the exception
).Sum(k => k.Qte);
var q = ctx.Quotas.Where(k => k.Id == item.IdQuota).FirstOrDefault();
return q.QteMaxJour >= item.Qte && q.QteMaxSemaine >= qteSem;
}
}
private static int GetIso8601WeekOfYear(DateTime time)
{
DayOfWeek day = System.Globalization.CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
{
time = time.AddDays(3);
}
return System.Globalization.CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}
private bool DateReservationOnTheSameWeek(DateTime date)
{
return GetIso8601WeekOfYear(this.DateReservation) == GetIso8601WeekOfYear(date);
}
}
I can't figure out why this predicate fails.
EDIT: I can't calculate the predicate out of the query because, it depends on k that is part of the lambda function.
Perhaps using SqlFunctions.DatePart and ISO_WEEK will help
using (var ctx = new SchedulingToolContext())
{
float qteSem = ctx.Reservations.Where(k => k.CltNameAlpha == item.CltNameAlpha
&& k.DateReservation.Year == item.DateReservation.Year
&& SqlFunctions.DatePart("isowk", this.DateReservation) == SqlFunction.DatePart("isowk", k.DateReservation)
).Sum(k => k.Qte);
var q = ctx.Quotas.Where(k => k.Id == item.IdQuota).FirstOrDefault();
return q.QteMaxJour >= item.Qte && q.QteMaxSemaine >= qteSem;
}
I have collection of items which are having one enum property list.
Original property looks like
public class Content {
List<State> States {get; set;}
}
where 'State' is enum with almost 15 options.
While iterating collection of Content objects, I want to check it States property has certain values like State.Important and State.Updated exists in States and set another string from it.
something like
if(item.States.Has(State.Important) && item.States.Has(State.Updated))
string toProcess = "Do";
How to do this using Linq or Lambda ?
This should work if you must use Linq:
if (item.States.Any(state => state == State.Important) && item.States.Any(state => state == State.Updated))
Otherwise just use Contains() like #ElRonnoco says.
(However if your states are flags (powers of 2), then this answer will be slightly different.)
The trouble with this approach is that it iterates over the collection fully twice if neither of the states are set. If this happens often, it will be slower than it could be.
You can solve it without linq in a single pass like so:
bool isUpdated = false;
bool isImportant = false;
foreach (var state in item.States)
{
if (state == State.Important)
isImportant = true;
else if (state == State.Updated)
isUpdated = true;
if (isImportant && isUpdated)
break;
}
if (isImportant && isUpdated)
{
// ...
}
This is unlikely to be an issue unless you have very large lists which often don't have either of the target states set, so you're probably best off using El Ronnoco's solution anyway.
If you have a lot of states to deal with, you could simplify things by writing an extension method like so:
public static class EnumerableExt
{
public static bool AllPredicatesTrueOverall<T>(this IEnumerable<T> self, params Predicate<T>[] predicates)
{
bool[] results = new bool[predicates.Length];
foreach (var item in self)
{
for (int i = 0; i < predicates.Length; ++i)
if (predicates[i](item))
results[i] = true;
if (results.All(state => state))
return true;
}
return false;
}
I had some difficulty coming up for a name for this. It will return true if for each predicate there is at least one item in the sequence for which the predicate is true. But that's a bit long for a method name... ;)
Then your example would become:
if (item.States.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
Here's some sample code that uses it:
enum State
{
Unknown,
Important,
Updated,
Deleted,
Other
}
void run()
{
IEnumerable<State> test1 = new[]
{
State.Important,
State.Updated,
State.Other,
State.Unknown
};
if (test1.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
Console.WriteLine("test1 passes.");
else
Console.WriteLine("test1 fails.");
IEnumerable<State> test2 = new[]
{
State.Important,
State.Other,
State.Other,
State.Unknown
};
if (test2.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
Console.WriteLine("test2 passes.");
else
Console.WriteLine("test2 fails.");
// And to show how you can use any number of predicates:
bool result = test1.AllPredicatesTrueOverall
(
state => state == State.Important,
state => state == State.Updated,
state => state == State.Other,
state => state == State.Deleted
);
}
But perhaps the easiest is to write an extension method for IEnumerable<State> (if you only have the one state enum to worry about):
public static class EnumerableStateExt
{
public static bool AllStatesSet(this IEnumerable<State> self, params State[] states)
{
bool[] results = new bool[states.Length];
foreach (var item in self)
{
for (int i = 0; i < states.Length; ++i)
if (item == states[i])
results[i] = true;
if (results.All(state => state))
return true;
}
return false;
}
}
Then your original code will become:
if (item.States.AllStatesSet(State.Important, State.Updated))
and you can easily specify more states:
if (item.States.AllStatesSet(State.Important, State.Updated, State.Deleted))
You don't need Linq. I don't thinq
if(item.States.Contains(State.Important) && item.States.Contains(State.Updated))
string toProcess = "Do";
http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx
List has a Contains method, so your code would be
if(item.States.Contains(State.Important) && item.States.Contains(State.Updated))
string toProcess = "Do";
I see no real benefit in using Linq or a lambda expression here...
You could go with
!(new List<States>{State.Important, State.Updated}.Except(item.States).Any());
It's not really shorter, but easier if you have a huge number of states to check.
As long as you want to check that the item has all states needed, you just have to add new States to the first list.
var res = (from items in item
where items.States.Has(State.Important) && items.States.Has(State.Updated)
select new { NewProcess = "Do" }).ToList();
foreach (var result in res)
{
string result = result.NewProcess
}
Try this
Maybe you could consider using your enum as a set of flags, i.e. you can combine multiple states without having a list:
[Flags]
public enum State
{
Important = 1,
Updated = 2,
Deleted = 4,
XXX = 8
....
}
public class Content
{
public State MyState { get; set; }
}
if ((myContent.MyState & State.Important) == State.Important
&& (myContent.MyState & State.Updated) == State.Updated)
{
// Important AND updated
}
Some sort of following implementation
Content obj = new Content();
obj.States = SomeMethod();
if(obj.States.Any(h => h == State.Important) && obj.States.Any(h => h == State.Updated))
{
string toProcess = "Do";
}