Create grouping that includes missing keys - c#

I have an IEnumerable<T> that has a Created field, which is a date.
There can be multiple T's per date and sometimes there are no T's for a given date.
Currently I'm grouping these by the date, which gives me all the dates that have at least one T, and the T's under them.
What I want though, is something I can use as part of a query that will get me all dates within a range, regardless of whether there are any T's with the given date.
Current Code:
var adjustments = DAL.GetAdjustmentsInDateRange(Start, End);
from adjustment in adjustments
group adjustment by adjustment.Created.Date into adjustmentsByDay
orderby adjustmentsByDay.Key descending
select ....
Here, adjustmentsByDay doesn't have all dates between Start and End. What I want is for it to include them, with no elements.
How can I do that?

You could left join adjustments with a list of all dates before grouping, like so:
var adjustments = DAL.GetAdjustmentsInDateRange(Start, End);
// Get all unique dates in time span
IEnumerable<DateTime> dates = GetAllDates(Start, End);
var query = (from date in dates
join adjustment in adjustments
on date.Date equals adjustment.Created.Date into a
from adjustment in a.DefaultIfEmpty()
select new {date.Date, adjustment}
).GroupBy(i=>i.Date).OrderBy(g=>g.Key);

I've put together a general-purpose LINQ-to-objects extension method to insert missing things into a sequence:
public static IEnumerable<T> InsertMissing<T, U>(this IEnumerable<T> source,
Func<T, U> key, Func<U, U> increment, Func<U, T> create)
{
bool first = true;
U last = default(U);
foreach (var ig in source)
{
U current = key(ig);
if (first)
{
first = false;
last = current;
yield return ig;
continue;
}
while (!(last = increment(last)).Equals(current))
{
yield return create(last);
}
yield return ig;
}
}
You'll also need a custom implementation of IGrouping:
class EmptyGrouping<K, E> : IGrouping<K, E> {
public K Key { get; set; }
public IEnumerator<E> GetEnumerator() {
return Enumerable.Empty<E>().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return this.GetEnumerator();
}
}
Then you'll need to end your query after the orderby, follow it with this call, and then put your select afterwards:
var allGroups = query.InsertMissing(
// Key selector
g => g.Key,
// Get next desired key from current key
d => d.AddDays(-1),
// Create item for missing key
d => new EmptyGrouping<DateTime,YourAdjustmentType>{ Key = d });
This will go haywire if your keys aren't ordered or if one of them doesn't fall in the correct place (e.g. in your case, isn't on midnight).
This has the advantage of not needing multiple queries on the original source to determine the min/max values in order to generate a list of keys, and then a further query to join and get the data.

Related

Sublists of consecutive elements that fit a condition in a list c# linq

So suppose we have a parking(represented as a dictionary<int,bool> :
Every parking lot has its id and a boolean(free,filled).
This way:
Dictionary<int,bool> parking..
parking[0]= true // means that the first parking lot is free
My question is i want to get the all sublist of consecutive elements that matchs in a condition : parking-lot is free.
First i can get elements that fits in this condition easy:
parking.Where(X => X.Value).Select(x => x.Key).ToList();
But then using linq operations i dont know how to get the first generated list that matchs in.
Can i do this without thousand of foreach-while loops checking iterating one by one, is there a easier way with linq?
This method gets a list of consecutive free parking lots
data:
0-free,
1-free,
2-filled ,
3-free
The results will be two lists:
First One will contain => 0 ,1
Second One will contain=> 3
These are the list of consecutive of parking lots that are free.
public List<List<int>> ConsecutiveParkingLotFree(int numberOfConsecutive){}
You can always write your own helper function to do things like this. For example
public static IEnumerable<List<T>> GroupSequential<T, TKey>(
this IEnumerable<T> self,
Func<T, bool> condition)
{
var list = new List<T>();
using var enumerator = self.GetEnumerator();
if (enumerator.MoveNext())
{
var current = enumerator.Current;
var oldValue = condition(current);
if (oldValue)
{
list.Add(current);
}
while (enumerator.MoveNext())
{
current = enumerator.Current;
var newValue = condition(current);
if (newValue)
{
list.Add(current);
}
else if (oldValue)
{
yield return list;
list = new List<T>();
}
oldValue = newValue;
}
if (list.Count > 0)
{
yield return list;
}
}
}
This will put all the items with a true-value in a list. When a true->false transition is encountered the list is returned and recreated. I would expect that there are more compact ways to write functions like this, but it should do the job.
You can apply GroupWhile solution here.
parking.Where(X => X.Value)
.Select(x => x.Key)
.GroupWhile((x, y) => y - x == 1)
.ToList()

Compare 2 lists with LINQ

I want to get all date values from dateRange where each datetime is the DayOfWeek.Friday,..Monday,..Sunday.
The Intersect did not work because I guess those 2 lists are not real intersections... and they have a different type: DateTime vs. Enum.DayOfWeek
This gives me only all fridays but I also want the Mondays,Sundays... without using the OR operator.
var dateRange = _dateService.GetDateRange(startDate, endDate).Where(d => visibleWeekDays.Any(v => d.DayOfWeek == v)).ToList();
These are both lists I have to compare somehow:
IEnumerable<DateTime> dateRange = _dateService.GetDateRange(startDate, endDate);
IEnumerable<DayOfWeek> visibleWeekDays = new List<DayOfWeek>
{
DayOfWeek.Friday,
DayOfWeek.Monday,
DayOfWeek.Sunday,
};
Please DO not write the full ling query as solution.
Just write the linq extension methods in random order I have to use to solve the riddle.
Just for the funs and learning sake :)
UPDATE
See my input datetime values and the output I want:
BUT be aware, the visibleWeekDays list is not static. There can be a dynamic number of values in this collection. Therefore I can and do not want to use the && or || operator.
When you find yourself in a position of wanting an intersection, but where the types of the two collections aren't the same, it usually means you want a Join. An intersection, by definition, is a join in which the two collections are of the same type, where the key selector is "itself", and where the result selector just picks one of the items at random (since they must be equal, by the definition of intersection). Since not all of these restrictions apply to you all you need to do is step out to the more general Join.
Just as a demonstration of this, here is an implementation of Intersect using just Join:
public static IEnumerable<T> Intersect<T>(this IEnumerable<T> first
, IEnumerable<T> second, IEqualityComparer<T> comparer)
{
return first.Join(second, x => x, x => x, (a, b) => a, comparer);
}
The DayOfWeek can just select itself as a key, and then you just need a method of getting a DayOfWeek object out of a DateTime for the key selector. For your result selector you only need to grab the DateTime object; you shouldn't need the DayOfWeek object anymore.
Whenever you see yourself writing a LINQ solution that has a Where(x => collection.Any(... or Contains or some other search operation inside of a Where odds are you should be using a Join instead, if applicable (you should at least ask yourself if you should be using a Join).
Since you don't want a full implementation I'll put it below in a spoiler tag. Don't look at it if you want to write the code yourself:
public static IEnumerable FilterDaysOfWeek(IEnumerable dates
, IEnumerable daysOfWeek)
{
return dates.Join(daysOfWeek
, date => date.DayOfWeek
, day => day
, (date, day) => date);
}
If you convert DayOfWeek to an integer, you can do a simple > and < comparison to get valid values, so just a .Where query should be good.
You could use Contains on the visibleWeekDays in your Where clause.
If you want to get all the dates in GetDateRange that have a DayOfWeek that matches those in visibleWeekdays you could use the following Linq statement:
_dateService.GetDateRange(startDate, endDate)
.Where(d=> visibleWeekdays.Contains(d.DayOfWeek));
Below is a full test of this in action:
class Test
{
static void Main(string[] args)
{
var weekdays = new[] { DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday };
var result = GetDateRange(DateTime.Today, DateTime.Today.AddDays(14))
.Where(d => weekdays.Contains(d.DayOfWeek));
}
public static IEnumerable<DateTime> GetDateRange(DateTime start, DateTime end)
{
DateTime date = start;
do
{
yield return date;
date = date.AddDays(1);
}
while (date < end);
}
}

LINQ query and Array of string

I have a array of string say:
String[] Fields=new String[]{RowField,RowField1}
In which I can use the below query to get the values by specifying the values is query i.e RowField and RowField1:
var Result = (
from x in _dataTable.AsEnumerable()
select new
{
Name = x.Field<object>(RowField),
Name1 = x.Field<object>(RowField1)
})
.Distinct();
But if suppose I have many values in the Array like:
String[] Fields= new String[]
{
RowField,
RowField1,
RowField2,
.......
RowField1000
};
How can I use the query here without specifying each of the rowfield in the query?
How can i iterate through the array items inside the LINQ?
var Result = (
from x in _dataTable.AsEnumerable()
select (
from y in Fields
select new KeyValuePair<string, object>(y, x))
.ToDictionary())
.Distinct(DictionariesComparer);
You'll also need to write your own .ToDictionary() extension method and DictionariesComparer method (as Dictionary doesn't implement IEquatable).
Essentially, you want to retrieve specific fields from a DataTable without hardcoding the field names.
The following code will return a single dictionary object per row with the fields you specify in your array. There is no need to create additional extension methods or comparers:
var result = (from row in _dataTable.AsEnumerable()
let projection = from fieldName in fields
select new {Name = fieldName, Value = row[fieldName]}
select projection.ToDictionary(p=>p.Name,p=>p.Value));
The inner select picks the field values you need from each table row and stores them in the projection variable. The outer select converts this variable in a Dictionary
You can iterate over the result to get specific fields like this:
foreach (var row in result)
{
Console.WriteLine(row["field1"]);
}
EDIT:
The above code doesn't return distinct values. It is possible to return distinct values without writing a special comparer using group by but the code is not very pretty:
var result = (from row in table.AsEnumerable()
let projection = from fieldName in fields
select new { Name = fieldName, Value = row[fieldName] }
group projection by projection.Aggregate((v, p) =>
new {
Name = v.Name + p.Name,
Value = (object)String.Format("{0}{1}", v.Value, p.Value)
}) into g
select g.FirstOrDefault().ToDictionary(p=>p.Name,p=>p.Value));
The Aggregate creates a new projection whose Name and Value properties are the concatenation of all name and value fields. The result of the aggregate is used to group all rows and return the first row of each group. It works but it is definitely ugly.
It would be better to create a simple DictionaryComparer like the following code:
public class DictionaryComparer<TKey,TValue>: EqualityComparer<Dictionary<TKey,TValue>>
{
public override bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
{
//True if both sequences of KeyValuePair items are equal
var sequenceEqual = x.SequenceEqual(y);
return sequenceEqual;
}
public override int GetHashCode(Dictionary<TKey, TValue> obj)
{
//Quickly detect differences in size, defer to Equals for dictionaries
//with matching sizes
return obj.Count;
}
}
This allows you to write:
var result = (from row in table.AsEnumerable()
let projection = from fieldName in fields
select new {Name = fieldName, Value = row[fieldName]}
select projection.ToDictionary(p=>p.Name,p=>p.Value))
.Distinct(new DictionaryComparer<string, object>());
There is no foreach linq expression. I typically create my own extension method
Something along the lines of:
public static void Foreach<T>(this IEnumerable<T> items, Action<T> action)
{
foreach(T t in items)
{
action(t);
}
}
However beware if you're planning on using this with Linq2SQL as it could create a lot of db hits!

how to handle source data changes in LINQ?

i have a collection of items where each item has a "date" field (the code is below).
i am trying to fill in any gaps in dates in the collection using LINQ. in particular, i want the resulting sequence to contain all days between the first and the last day in the original sequence.
in addition to this, my resulting LINQ query should be able to handle any modifications of the original sequence. that is i cannot calculate the minimal and the maximal dates ahead of time.
so i tried the code below but it fails when it tries to calculate Min and Max of the sequence. i am looking for a "lazy" alternative.
thanks for any help
konstantin
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace consapp
{
class C
{
public DateTime date;
public int? value;
}
static class Program
{
static IEnumerable<C> dates(DateTime d0, DateTime d1)
{
for (var d = d0; d <= d1; d = d.AddDays(1))
{
yield return new C { date = d };
}
}
static void Main(string[] args)
{
var xs = new ObservableCollection<C>();
var q = from d in dates(xs.Min(y => y.date), xs.Max(y => y.date))
join x in xs on d.date equals x.date into js
from j in js.DefaultIfEmpty()
orderby d.date
select new { date = d.date, value = j != null ? j.value : null };
xs.Add(new C { date = DateTime.Parse("11/10/11") });
xs.Add(new C { date = DateTime.Parse("02/02/11") });
xs.Add(new C { date = DateTime.Parse("11/24/11") });
xs.Add(new C { date = DateTime.Parse("09/09/11") });
xs.Add(new C { date = DateTime.Parse("11/10/11") });
foreach (var x in q)
{
Console.WriteLine(x.date.ToShortDateString());
}
}
}
}
I'm not absolutely positive, but:
var q = from d in dates(xs.Min(y => y.date), xs.Max(y => y.date))
I believe that the "dates" method will be called immediately, and the rest of the LINQ query (including the iterator from dates() itself) will be built up around the result from that method. So you are going to have to pre-populate xs with the data you are interested in.
This is because LINQ essentially works by wrapping enumerables in other enumerables. In order for it to do this, it must start with an enumerable. In order to do that, it must call your order() method, which requires supplying its arguments immediately, so that it can receive the enumerable object that it will be wrapping in other enumerables. So the xs.Min and xs.Max methods will be called when that line of code is reached, but nothing else in the query will actually be processed.
A workaround would be to have your dates() method actually receive the ObservableCollection and call Min/Max itself. Because this will happen in the generated iterator, execution of those calls will be deferred as you expect.
Standard LINQ implementation based on IEnumerable<T> cannot handle data sources that are changing such as ObservableCollection. The reason why your example fails is that it will try to evaluate the data source (and call the dates function and Min and Max operators) when you define the query (but the data source doesn't contain any data at that point).
One option is to use an alternative LINQ implementation that works with ObservableCollection and can automatically update the result when the source changes. As far as I know Bindable LINQ project should be able to do that.
Another (simpler) option is to turn your query into a method and call the method repeatedly (to update the result) when you know that the data source has changed. You'd have to make ObservableCollection a private field and the method would simply run using the data currently stored in the collection:
private ObservableCollection source;
void UpdateResults() {
var q = /* The query goes here */
// Do something with the result of the query
}

How to perform .Max() on a property of all objects in a collection and return the object with maximum value [duplicate]

This question already has answers here:
How to use LINQ to select object with minimum or maximum property value
(20 answers)
Closed 7 years ago.
I have a list of objects that have two int properties. The list is the output of another linq query. The object:
public class DimensionPair
{
public int Height { get; set; }
public int Width { get; set; }
}
I want to find and return the object in the list which has the largest Height property value.
I can manage to get the highest value of the Height value but not the object itself.
Can I do this with Linq? How?
We have an extension method to do exactly this in MoreLINQ. You can look at the implementation there, but basically it's a case of iterating through the data, remembering the maximum element we've seen so far and the maximum value it produced under the projection.
In your case you'd do something like:
var item = items.MaxBy(x => x.Height);
This is better (IMO) than any of the solutions presented here other than Mehrdad's second solution (which is basically the same as MaxBy):
It's O(n) unlike the previous accepted answer which finds the maximum value on every iteration (making it O(n^2))
The ordering solution is O(n log n)
Taking the Max value and then finding the first element with that value is O(n), but iterates over the sequence twice. Where possible, you should use LINQ in a single-pass fashion.
It's a lot simpler to read and understand than the aggregate version, and only evaluates the projection once per element
This would require a sort (O(n log n)) but is very simple and flexible. Another advantage is being able to use it with LINQ to SQL:
var maxObject = list.OrderByDescending(item => item.Height).First();
Note that this has the advantage of enumerating the list sequence just once. While it might not matter if list is a List<T> that doesn't change in the meantime, it could matter for arbitrary IEnumerable<T> objects. Nothing guarantees that the sequence doesn't change in different enumerations so methods that are doing it multiple times can be dangerous (and inefficient, depending on the nature of the sequence). However, it's still a less than ideal solution for large sequences. I suggest writing your own MaxObject extension manually if you have a large set of items to be able to do it in one pass without sorting and other stuff whatsoever (O(n)):
static class EnumerableExtensions {
public static T MaxObject<T,U>(this IEnumerable<T> source, Func<T,U> selector)
where U : IComparable<U> {
if (source == null) throw new ArgumentNullException("source");
bool first = true;
T maxObj = default(T);
U maxKey = default(U);
foreach (var item in source) {
if (first) {
maxObj = item;
maxKey = selector(maxObj);
first = false;
} else {
U currentKey = selector(item);
if (currentKey.CompareTo(maxKey) > 0) {
maxKey = currentKey;
maxObj = item;
}
}
}
if (first) throw new InvalidOperationException("Sequence is empty.");
return maxObj;
}
}
and use it with:
var maxObject = list.MaxObject(item => item.Height);
Doing an ordering and then selecting the first item is wasting a lot of time ordering the items after the first one. You don't care about the order of those.
Instead you can use the aggregate function to select the best item based on what you're looking for.
var maxHeight = dimensions
.Aggregate((agg, next) =>
next.Height > agg.Height ? next : agg);
var maxHeightAndWidth = dimensions
.Aggregate((agg, next) =>
next.Height >= agg.Height && next.Width >= agg.Width ? next: agg);
And why don't you try with this ??? :
var itemsMax = items.Where(x => x.Height == items.Max(y => y.Height));
OR more optimise :
var itemMaxHeight = items.Max(y => y.Height);
var itemsMax = items.Where(x => x.Height == itemMaxHeight);
mmm ?
The answers so far are great! But I see a need for a solution with the following constraints:
Plain, concise LINQ;
O(n) complexity;
Do not evaluate the property more than once per element.
Here it is:
public static T MaxBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
.Aggregate((max, next) => next.Item2.CompareTo(max.Item2) > 0 ? next : max).Item1;
}
public static T MinBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
.Aggregate((max, next) => next.Item2.CompareTo(max.Item2) < 0 ? next : max).Item1;
}
Usage:
IEnumerable<Tuple<string, int>> list = new[] {
new Tuple<string, int>("other", 2),
new Tuple<string, int>("max", 4),
new Tuple<string, int>("min", 1),
new Tuple<string, int>("other", 3),
};
Tuple<string, int> min = list.MinBy(x => x.Item2); // "min", 1
Tuple<string, int> max = list.MaxBy(x => x.Item2); // "max", 4
I believe that sorting by the column you want to get the MAX of and then grabbing the first should work. However, if there are multiple objects with the same MAX value, only one will be grabbed:
private void Test()
{
test v1 = new test();
v1.Id = 12;
test v2 = new test();
v2.Id = 12;
test v3 = new test();
v3.Id = 12;
List<test> arr = new List<test>();
arr.Add(v1);
arr.Add(v2);
arr.Add(v3);
test max = arr.OrderByDescending(t => t.Id).First();
}
class test
{
public int Id { get; set; }
}
In NHibernate (with NHibernate.Linq) you could do it as follows:
return session.Query<T>()
.Single(a => a.Filter == filter &&
a.Id == session.Query<T>()
.Where(a2 => a2.Filter == filter)
.Max(a2 => a2.Id));
Which will generate SQL like follows:
select *
from TableName foo
where foo.Filter = 'Filter On String'
and foo.Id = (select cast(max(bar.RowVersion) as INT)
from TableName bar
where bar.Name = 'Filter On String')
Which seems pretty efficient to me.
Based on Cameron's initial answer, here is what I've just added at my enhanced version of SilverFlow library's FloatingWindowHost (copying from FloatingWindowHost.cs at http://clipflair.codeplex.com source code)
public int MaxZIndex
{
get {
return FloatingWindows.Aggregate(-1, (maxZIndex, window) => {
int w = Canvas.GetZIndex(window);
return (w > maxZIndex) ? w : maxZIndex;
});
}
}
private void SetTopmost(UIElement element)
{
if (element == null)
throw new ArgumentNullException("element");
Canvas.SetZIndex(element, MaxZIndex + 1);
}
Worth noting regarding the code above that Canvas.ZIndex is an attached property available for UIElements in various containers, not just used when being hosted in a Canvas (see Controlling rendering order (ZOrder) in Silverlight without using the Canvas control). Guess one could even make a SetTopmost and SetBottomMost static extension method for UIElement easily by adapting this code.
You can also upgrade Mehrdad Afshari's solution by rewriting the extention method to faster (and better looking) one:
static class EnumerableExtensions
{
public static T MaxElement<T, R>(this IEnumerable<T> container, Func<T, R> valuingFoo) where R : IComparable
{
var enumerator = container.GetEnumerator();
if (!enumerator.MoveNext())
throw new ArgumentException("Container is empty!");
var maxElem = enumerator.Current;
var maxVal = valuingFoo(maxElem);
while (enumerator.MoveNext())
{
var currVal = valuingFoo(enumerator.Current);
if (currVal.CompareTo(maxVal) > 0)
{
maxVal = currVal;
maxElem = enumerator.Current;
}
}
return maxElem;
}
}
And then just use it:
var maxObject = list.MaxElement(item => item.Height);
That name will be clear to people using C++ (because there is std::max_element in there).

Categories

Resources