I have a query in which i want to check where two two rows pointing to same time or overlapping time time span
e.g
object1.startDate=07/12/2015
object1.endDate=12/12/2015
object2.startDate=08/12/2015
object2.endDate=08/12/2015
how can we compare duration of object1 and object2 in linq query to check where the two object have same/overlapping time period
You can do this by the following logic:
if(object1.endDate >= object2.startDate && object1.startDate <= object2.endDate)
{
//overlaping logic here
}
Usage example:
public class test
{
public int id;
public DateTime st;
public DateTime et;
}
class Program
{
static void Main(string[] args)
{
var list = new List<test>()
{
new test{id = 1, st = DateTime.Parse("2015-10-15"), et = DateTime.Parse("2015-10-18")},
new test{id = 2, st = DateTime.Parse("2015-10-16"), et = DateTime.Parse("2015-10-17")},
new test{id = 3, st = DateTime.Parse("2015-10-17"), et = DateTime.Parse("2015-10-17")},
new test{id = 4, st = DateTime.Parse("2015-10-25"), et = DateTime.Parse("2015-10-26")},
};
var result = (from l1 in list
from l2 in list
where l1.id > l2.id && l1.et >= l2.st && l1.st <= l2.et
select new { l1= l1.id, l2 = l2.id}).ToList();
}
}
Output:
+ [0] { l1 = 2, l2 = 1 } <Anonymous Type>
+ [1] { l1 = 3, l2 = 1 } <Anonymous Type>
+ [2] { l1 = 3, l2 = 2 } <Anonymous Type>
Related
I'm a beginner in linq.
I would like to write this query in linq.
Select SUM(importo), anno From(
Select anno, Importo from[Archivio].[dbo].[AAA]
union
Select anno, importo From[Archivio].[dbo].[BBB]) as prova
group by Anno order by anno.
I wrote this:
DataTable Tab_AAA = DS_AAA.Tables[0];
DataTable Tab_BBB = DS_BBB.Tables[0];
IEnumerable<DataRow> query =
(from A in DS_AAA.AsEnumerable()
select A).Union(from B in DS_BBB.AsEnumerable()
select B)
now I can not enter the sum and the group by.
Thank you all
the objects that you are using must be something like this
public class AnnoImporto
{
private int _anno;
private double _importo;
private string _altro;
public AnnoImporto(int aAnno, double aImp, string aAltro)
{
_anno = aAnno;
_importo = aImp;
_altro = aAltro;
}
public int Anno
{
get { return _anno; }
set { _anno = value; }
}
public double Importo
{
get { return _importo; }
set { _importo = value; }
}
public string Altro
{
get { return _altro; }
set { _altro = value; }
}
}
surely you have the year and the amount, think to "altro" as generic eventual other fields. I built two lists of AnnoImporto objects, but the following code will work even on lists of different object (of course, as I just said, just having year and amount...)
List<AnnoImporto> aaa = new List<AnnoImporto>();
List<AnnoImporto> bbb = new List<AnnoImporto>();
aaa.Add(new AnnoImporto(2015, 10,"blabla"));
aaa.Add(new AnnoImporto(2014, 20, "blabla"));
aaa.Add(new AnnoImporto(2013, 15, "blabla"));
aaa.Add(new AnnoImporto(2012, 5, "blabla"));
aaa.Add(new AnnoImporto(2011, 40, "blabla"));
bbb.Add(new AnnoImporto(2011, 50, "blabla"));
bbb.Add(new AnnoImporto(2013, 20, "blabla"));
bbb.Add(new AnnoImporto(2015, 15, "blabla"));
var aaa_mod = from a in aaa
select new
{
Anno = a.Anno,
Importo = a.Importo
};
var bbb_mod = from b in bbb
select new
{
Anno = b.Anno,
Importo = b.Importo
};
var unione = aaa_mod.Union(bbb_mod);
Console.WriteLine("controlliamo la nuova lista:");
foreach (var p in unione)
{
Console.WriteLine($"{p.Anno}, {p.Importo}");
}
var somme = from p in unione
orderby p.Anno
group p.Importo by p.Anno into q
select new
{
Anno = q.Key,
ImportoTotale = q.Sum()
};
Console.WriteLine("controlliamo le somme:");
foreach (var p in somme)
{
Console.WriteLine($"{p.Anno}, {p.ImportoTotale}");
}
I followed step by step your query: first I selected in aaa and in bbb only the fields you need, I made the union of the results and I checked it. Then I did the group by to calculate sums by year.
Given an enumeration of records in the format:
Name (string)
Amount (number)
For example:
Laverne 4
Lenny 2
Shirley 3
Squiggy 5
I want to group the records, so that each group's total Amount does not exceed some limit-per-group. For example, 10.
Group 1 (Laverne,Lenny,Shirley) with Total Amount 9
Group 2 (Squiggy) with Total Amount 5
The Amount number is guaranteed to always be less than the grouping limit.
If you allow for captured variables to maintain state, then it becomes easier. If we have:
int limit = 10;
Then:
int groupTotal = 0;
int groupNum = 0;
var grouped = records.Select(r =>
{
int newCount = groupTotal + r.Amount;
if (newCount > limit)
{
groupNum++;
groupTotal = r.Amount;
}
else
groupTotal = newCount;
return new{Records = r, Group = groupNum};
}
).GroupBy(g => g.Group, g => g.Records);
It's O(n), and just a Select and a GroupBy, but the use of captured variables may not be as portable across providers as one may want though.
For linq-to-objects though, it's fine.
Here I have a solution using only LINQ functions:
// Record definition
class Record
{
public string Name;
public int Amount;
public Record(string name, int amount)
{
Name = name;
Amount = amount;
}
}
// actual code for setup and LINQ
List<Record> records = new List<Record>()
{
new Record("Laverne", 4),
new Record("Lenny", 2),
new Record("Shirley", 3),
new Record("Squiggy", 5)
};
int groupLimit = 10;
// the solution
List<Record[]> test =
records.GroupBy(record => records.TakeWhile(r => r != record)
.Concat(new[] { record })
.Sum(r => r.Amount) / (groupLimit + 1))
.Select(g => g.ToArray()).ToList();
This gives the correct result:
test =
{
{ [ "Laverne", 4 ], [ "Lenny", 2 ], [ "shirley", 3 ] },
{ [ "Squiggly", 5 ] }
}
The only downside is that this is O(n2). It essentially groups by the index of the group (as defined by using the sum of the record up to the current one). Note that groupLimit + 1 is needed so that we actually include groups from 0 to groupLimit, inclusive.
I'm trying to find a way of making it prettier, but it doesn't look easy.
A dotnet fiddle with a solution using Aggregate:
https://dotnetfiddle.net/gVgONH
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
// Record definition
public class Record
{
public string Name;
public int Amount;
public Record(string name, int amount)
{
Name = name;
Amount = amount;
}
}
public static void Main()
{
// actual code for setup and LINQ
List<Record> records = new List<Record>()
{
new Record("Alice", 1), new Record("Bob", 5), new Record("Charly", 4), new Record("Laverne", 4), new Record("Lenny", 2), new Record("Shirley", 3), new Record("Squiggy", 5)}
;
int groupLimit = 10;
int sum = 0;
var result = records.Aggregate(new List<List<Record>>(), (accumulated, next) =>
{
if ((sum + next.Amount >= groupLimit) || accumulated.Count() == 0)
{
Console.WriteLine("New team: " + accumulated.Count());
accumulated.Add(new List<Record>());
sum = 0;
}
sum += next.Amount;
Console.WriteLine("New member {0} ({1}): adds up to {2} ", next.Name, next.Amount, sum);
accumulated.Last().Add(next);
return accumulated;
}
);
Console.WriteLine("Team count: " + result.Count());
}
}
With output:
New team: 0
New member Alice (1): adds up to 1
New member Bob (5): adds up to 6
New team: 1
New member Charly (4): adds up to 4
New member Laverne (4): adds up to 8
New team: 2
New member Lenny (2): adds up to 2
New member Shirley (3): adds up to 5
New team: 3
New member Squiggy (5): adds up to 5
Team count: 4
There is no 'performant' way to do this with the built in Linq operators that I am aware of. You could create your own extension method, though:
public static class EnumerableExtensions
{
public static IEnumerable<TResult> GroupWhile<TSource, TAccumulation, TResult>(
this IEnumerable<TSource> source,
Func<TAccumulation> seedFactory,
Func<TAccumulation, TSource, TAccumulation> accumulator,
Func<TAccumulation, bool> predicate,
Func<TAccumulation, IEnumerable<TSource>, TResult> selector)
{
TAccumulation accumulation = seedFactory();
List<TSource> result = new List<TSource>();
using(IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while(enumerator.MoveNext())
{
if(!predicate(accumulator(accumulation, enumerator.Current)))
{
yield return selector(accumulation, result);
accumulation = seedFactory();
result = new List<TSource>();
}
result.Add(enumerator.Current);
accumulation = accumulator(accumulation, enumerator.Current);
}
if(result.Count > 0)
{
yield return selector(accumulation, result);
}
}
}
}
And then call it like this:
int limit = 10;
var groups =
records
.GroupWhile(
() => 0,
(a, x) => a + x,
(a) => a <= limit,
(a, g) => new { Total = a, Group = g });
The way it is currently written, if any single record exceeds that limit then that record is returned by itself. You could modify it to exclude records that exceed the limit or leave it as is and perform the exclusion with Where.
This solution has O(n) runtime.
Given:
class Item {
public int[] SomeMembers { get; set; }
}
var items = new []
{
new Item { SomeMembers = new [] { 1, 2 } }, //0
new Item { SomeMembers = new [] { 1, 2 } }, //1
new Item { SomeMembers = new [] { 1 } } //2
}
var secondList = new int[] { 1, 2, 3 };
I need to find all the Items in items with the most of it's SomeMembers occurring in secondList.
In the example above I would expect Items 0 and 1 to be returned but not 2.
I know I could do it with things like loops or Contains() but it seems there must be a more elegant or efficient way?
This can be written pretty easily:
var result = items.Where(item => item.SomeMembers.Count(secondList.Contains) * 2
>= item.SomeMembers.Length);
Or possibly (I can never guess whether method group conversions will work):
var result = items.Where(item => item.SomeMembers.Count(x => secondList.Contains(x)) * 2
>= item.SomeMembers.Length);
Or to pull it out:
Func<int, bool> inSecondList = secondList.Contains;
var result = items.Where(item => item.SomeMembers.Count(inSecondList) * 2
>= item.SomeMembers.Length);
If secondList becomes large, you should consider using a HashSet<int> instead.
EDIT: To avoid evaluating SomeMembers twice, you could create an extension method:
public static bool MajoritySatisfied<T>(this IEnumerable<T> source,
Func<T, bool> condition)
{
int total = 0, satisfied = 0;
foreach (T item in source)
{
total++;
if (condition(item))
{
satisfied++;
}
}
return satisfied * 2 >= total;
}
Then:
var result = items.Where(item => item.MajoritySatisfied(secondList.Contains));
I have a collection of items that contain an Enum (TypeCode) and a User object, and I need to flatten it out to show in a grid. It's hard to explain, so let me show a quick example.
Collection has items like so:
TypeCode | User
---------------
1 | Don Smith
1 | Mike Jones
1 | James Ray
2 | Tom Rizzo
2 | Alex Homes
3 | Andy Bates
I need the output to be:
1 | 2 | 3
Don Smith | Tom Rizzo | Andy Bates
Mike Jones | Alex Homes |
James Ray | |
I've tried doing this using foreach, but I can't do it that way because I'd be inserting new items to the collection in the foreach, causing an error.
Can this be done in Linq in a cleaner fashion?
I'm not saying it is a great way to pivot - but it is a pivot...
// sample data
var data = new[] {
new { Foo = 1, Bar = "Don Smith"},
new { Foo = 1, Bar = "Mike Jones"},
new { Foo = 1, Bar = "James Ray"},
new { Foo = 2, Bar = "Tom Rizzo"},
new { Foo = 2, Bar = "Alex Homes"},
new { Foo = 3, Bar = "Andy Bates"},
};
// group into columns, and select the rows per column
var grps = from d in data
group d by d.Foo
into grp
select new {
Foo = grp.Key,
Bars = grp.Select(d2 => d2.Bar).ToArray()
};
// find the total number of (data) rows
int rows = grps.Max(grp => grp.Bars.Length);
// output columns
foreach (var grp in grps) {
Console.Write(grp.Foo + "\t");
}
Console.WriteLine();
// output data
for (int i = 0; i < rows; i++) {
foreach (var grp in grps) {
Console.Write((i < grp.Bars.Length ? grp.Bars[i] : null) + "\t");
}
Console.WriteLine();
}
Marc's answer gives sparse matrix that can't be pumped into Grid directly.
I tried to expand the code from the link provided by Vasu as below:
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
return source.GroupBy(key1Selector).Select(
x => new
{
X = x.Key,
Y = source.GroupBy(key2Selector).Select(
z => new
{
Z = z.Key,
V = aggregate(from item in source
where key1Selector(item).Equals(x.Key)
&& key2Selector(item).Equals(z.Key)
select item
)
}
).ToDictionary(e => e.Z, o => o.V)
}
).ToDictionary(e => e.X, o => o.Y);
}
internal class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public string Function { get; set; }
public decimal Salary { get; set; }
}
public void TestLinqExtenions()
{
var l = new List<Employee>() {
new Employee() { Name = "Fons", Department = "R&D", Function = "Trainer", Salary = 2000 },
new Employee() { Name = "Jim", Department = "R&D", Function = "Trainer", Salary = 3000 },
new Employee() { Name = "Ellen", Department = "Dev", Function = "Developer", Salary = 4000 },
new Employee() { Name = "Mike", Department = "Dev", Function = "Consultant", Salary = 5000 },
new Employee() { Name = "Jack", Department = "R&D", Function = "Developer", Salary = 6000 },
new Employee() { Name = "Demy", Department = "Dev", Function = "Consultant", Salary = 2000 }};
var result5 = l.Pivot3(emp => emp.Department, emp2 => emp2.Function, lst => lst.Sum(emp => emp.Salary));
var result6 = l.Pivot3(emp => emp.Function, emp2 => emp2.Department, lst => lst.Count());
}
* can't say anything about the performance though.
You can use Linq's .ToLookup to group in the manner you are looking for.
var lookup = data.ToLookup(d => d.TypeCode, d => d.User);
Then it's a matter of putting it into a form that your consumer can make sense of. For instance:
//Warning: untested code
var enumerators = lookup.Select(g => g.GetEnumerator()).ToList();
int columns = enumerators.Count;
while(columns > 0)
{
for(int i = 0; i < enumerators.Count; ++i)
{
var enumerator = enumerators[i];
if(enumator == null) continue;
if(!enumerator.MoveNext())
{
--columns;
enumerators[i] = null;
}
}
yield return enumerators.Select(e => (e != null) ? e.Current : null);
}
Put that in an IEnumerable<> method and it will (probably) return a collection (rows) of collections (column) of User where a null is put in a column that has no data.
I guess this is similar to Marc's answer, but I'll post it since I spent some time working on it. The results are separated by " | " as in your example. It also uses the IGrouping<int, string> type returned from the LINQ query when using a group by instead of constructing a new anonymous type. This is tested, working code.
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 1, UserName = "Mike Jones"},
new { TypeCode = 1, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
var Columns = from i in Items
group i.UserName by i.TypeCode;
Dictionary<int, List<string>> Rows = new Dictionary<int, List<string>>();
int RowCount = Columns.Max(g => g.Count());
for (int i = 0; i <= RowCount; i++) // Row 0 is the header row.
{
Rows.Add(i, new List<string>());
}
int RowIndex;
foreach (IGrouping<int, string> c in Columns)
{
Rows[0].Add(c.Key.ToString());
RowIndex = 1;
foreach (string user in c)
{
Rows[RowIndex].Add(user);
RowIndex++;
}
for (int r = RowIndex; r <= Columns.Count(); r++)
{
Rows[r].Add(string.Empty);
}
}
foreach (List<string> row in Rows.Values)
{
Console.WriteLine(row.Aggregate((current, next) => current + " | " + next));
}
Console.ReadLine();
I also tested it with this input:
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 3, UserName = "Mike Jones"},
new { TypeCode = 3, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
Which produced the following results showing that the first column doesn't need to contain the longest list. You could use OrderBy to get the columns ordered by TypeCode if needed.
1 | 3 | 2
Don Smith | Mike Jones | Tom Rizzo
| James Ray | Alex Homes
| Andy Bates |
#Sanjaya.Tio I was intrigued by your answer and created this adaptation which minimizes keySelector execution. (untested)
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
var lookup = source.ToLookup(x => new {Key1 = key1Selector(x), Key2 = key2Selector(x)});
List<TKey1> key1s = lookup.Select(g => g.Key.Key1).Distinct().ToList();
List<TKey2> key2s = lookup.Select(g => g.Key.Key2).Distinct().ToList();
var resultQuery =
from key1 in key1s
from key2 in key2s
let lookupKey = new {Key1 = key1, Key2 = key2}
let g = lookup[lookupKey]
let resultValue = g.Any() ? aggregate(g) : default(TValue)
select new {Key1 = key1, Key2 = key2, ResultValue = resultValue};
Dictionary<TKey1, Dictionary<TKey2, TValue>> result = new Dictionary<TKey1, Dictionary<TKey2, TValue>>();
foreach(var resultItem in resultQuery)
{
TKey1 key1 = resultItem.Key1;
TKey2 key2 = resultItem.Key2;
TValue resultValue = resultItem.ResultValue;
if (!result.ContainsKey(key1))
{
result[key1] = new Dictionary<TKey2, TValue>();
}
var subDictionary = result[key1];
subDictionary[key2] = resultValue;
}
return result;
}
I have a collection of objects that each defined by a particular "group". How can I write a LINQ query to produce a count of each object grouped by "group".
As an example, lets say I have the following classes;
public class Release
{
int ReleaseNumber;
public ReleaseDetails[] details;
}
public class ReleaseDetails
{
string Group;
// other details omitted
}
For a given release, I'd like to be able to produce output like;
Release number 1 contains the following details;
- 17 records in Group A
- 12 records in Group B
- 6 records in Group C
Any help is much appreciated.
You can do something like
var q = from d in r.Details
group d by d.Group into counts
select new { Count = counts.Count(), Group = counts.Key };
Full example:
Release r = new Release
{
ReleaseNumber = 1
,
Details = new ReleaseDetails[]
{
new ReleaseDetails { Group = "a"},
new ReleaseDetails { Group = "a"},
new ReleaseDetails { Group = "b"},
new ReleaseDetails { Group = "c"},
new ReleaseDetails { Group = "d"},
new ReleaseDetails { Group = "d"},
new ReleaseDetails { Group = "e"},
}
};
var q = from d in r.Details
group d by d.Group into counts
select new { Count = counts.Count(), Group = counts.Key };
foreach (var count in q)
{
Console.WriteLine("Group {0}: {1}", count.Group, count.Count);
}
Here you go.
public class ReleaseDetails
{
public string Group { get; set; }
public ReleaseDetails() {}
public ReleaseDetails(string grp){Group = grp;}
}
var qry = new Release();
qry.details = new List<ReleaseDetails>();
qry.details.Add(new ReleaseDetails("A"));
qry.details.Add(new ReleaseDetails("A"));
qry.details.Add(new ReleaseDetails("B"));
qry.details.Add(new ReleaseDetails("C"));
qry.details.Add(new ReleaseDetails("C"));
qry.details.Add(new ReleaseDetails("B"));
var result = from x in qry.details
group x by x.Group into g
select new
{
Count = g.Count(),
Group = g.Key
};
//Or using Labmda
var result1 = qry.details.GroupBy(x => x.Group).Select(g => new { Count = g.Count(), Group = g.Key });