I have below data in my linq list.
StatusID Count MonthYear
======== ===== =========
1 0 Jan 2014
2 1 Feb 2013
1 2 Jan 2013
3 1 Dec 2014
2 0 Nov 2014
5 6 Jun 2015
Now my requirement is i need above list in below format. Where MonthYear column data is not fix. It can be any month and year data.
StatusID Jan 2013 Feb 2013 Jan 2014 Nov 2014 Dec 2014 Jun 2015
======== ======== ======== ======== ======== ======== ========
1 2 0 0 0 0 0
2 0 1 0 0 0 0
3 0 0 0 0 1 0
5 0 0 0 0 0 6
I read lots of solution on stackoverflow and even tried my own way but i was not get success. Below is code which i tried last.
var pvtData = new PivotData(new[] { "StatusID", "MonthYear" }, new SumAggregatorFactory("count"));
pvtData.ProcessData(finalList, (o, f) =>
{
var custData = (MontlyChartModified)o;
switch (f)
{
case "StatusID": return custData.StatusID;
case "MonthYear":
return custData.MonthYear;
case "count": return custData.count;
}
return null;
});
Please help me if you have any idea how to do dynamic pivoting.
Below is my code which i implemented. but when i debug and put watch on result var it shows me error "result The name 'result' does not exist in the current context"
public class MontlyChartModified
{
public int? StatusID { get; set; }
public int count { get; set; }
public string MonthYear { get; set; }
}
List<MontlyChartModified> finalList = new List<MontlyChartModified>();
finalList = (from x in Okdata
select new MontlyChartModified
{
StatusID = x.StatusID,
count = x.count,
MonthYear = x.Month.ToString() + " " + x.Year.ToString()
}).ToList();
var columns = new[] { "StatusID" }.Union(finalList.Select(a => a.MonthYear).OrderBy(a => a.Split(' ')[1]).ThenBy(a => a)).ToList();
var result = finalList.GroupBy(g => g.StatusID).OrderBy(g => g.Key).Select(g => columns.Select(c =>
{
if (c == "StatusID") return g.Key;
var val = g.FirstOrDefault(r => r.MonthYear == c);
return val != null ? val.count : 0;
}).ToList()).ToList();
my final result.
I managed to do it with grouping:
First, I create list of columns, sorted by year and month alphabetically:
var columns = new[] { "StatusID" }.Union(lst.Select(a => a.MonthYear).OrderBy(a => a.Split(' ')[1]).ThenBy(a => a)).ToList();
Second, results are groupped by StatusID and for each group I create List of values for each MonthYear column:
var result = lst.GroupBy(g => g.StatusID).OrderBy(g => g.Key).Select(g => columns.Select(c =>
{
if (c == "StatusID") return g.Key;
var val = g.FirstOrDefault(r => r.MonthYear == c);
return val != null ? val.Count : 0;
}).ToList()).ToList();
Input list is defined as follows:
var lst = new List<MontlyChartModified>()
{
new MontlyChartModified(){StatusID = 1, Count = 0, MonthYear = "Jan 2014"},
new MontlyChartModified(){StatusID = 2, Count = 1, MonthYear = "Feb 2013"},
new MontlyChartModified(){StatusID = 1, Count = 2, MonthYear = "Jan 2013"},
new MontlyChartModified(){StatusID = 3, Count = 1, MonthYear = "Dec 2014"},
new MontlyChartModified(){StatusID = 2, Count = 0, MonthYear = "Nov 2014"},
new MontlyChartModified(){StatusID = 5, Count = 6, MonthYear = "Jun 2015"},
};
class MontlyChartModified
{
public int StatusID { get; set; }
public int Count { get; set; }
public string MonthYear { get; set; }
}
result is List<List<int>>
Related
I have a data set that looks like below:
Option | Year | Month | Value
-------+------+-------+------
1 | 2011 | 12 | 0
-------+------+-------+------
1 | 2011 | 11 | 1
-------+------+-------+------
2 | 2012 | 6 | 0
-------+------+-------+------
2 | 2012 | 7 | 0
-------+------+-------+------
1 | 2011 | 6 | 2
The result set I am looking for is below :
Option | Year | ChangedCount
-------+------+-------------
1 | 2011 | 3
-------+------+-------------
2 | 2012 | 0
-------+------+-------------
Changed Count represents , if the value has changed in the same year between different months . so say if the value of 06 month was 2 and then 07 it changed to 1 , then changed count will be 1 . If the value for two months remains the same , then changedCount is 0
Here is what I have written so far
var changes = from ord in resultSet
group ord by new
{
ord.Year,
ord.Month,
ord.Option,
ord.Value,
}
into g
select new
{
Year = g.Key.Year,
changed = g.Count(x => x.Value == 0)
+ g.Count(x => x.Value == 1)
+ g.Count(x => x.Value == 2)
};
How do I run comparison for previous value in column ?
{0,1,2} Map ENUM values
This is what I understand from your explanation:
class Record
{
public int Option { get; set; }
public int Year { get; set; }
public int Month { get; set; }
public int Value { get; set; }
}
var resultSet = new List<Record> {
new Record { Option=1, Year=2011, Month=12, Value=0 },
new Record { Option=1, Year=2011, Month=11, Value=1 },
new Record { Option=2, Year=2012, Month=6, Value=0 },
new Record { Option=2, Year=2012, Month=7, Value=0 },
new Record { Option=1, Year=2011, Month=6, Value=2 },
};
Helper Method to count changes:
public static int changeCount(List<Record> Records)
{
int previous = Records[0].Value;
var result_change = 0;
//i used sorted records by month you can do not this if order is not sensitive
foreach (var rec in Records.OrderBy(x=>x.Month))
{
if (rec.Value != previous)
{
result_change++;
}
previous = rec.Value;
}
return result_change;
}
and the actual code :
var changes = resultSet.GroupBy(x => new { x.Year }).Select(g => new
{
Year = g.Key.Year,
changed =changeCount( g.ToList()),
Option = g.First().Option
}).ToList();
Result :
2011,3,1
2012,0,2
Try:
var changes = from ord in resultSet
group ord by new
{
ord.Option,
ord.Year,
}
into g
select new
{
Option = g.Key.Option,
Year = g.Key.Year,
ChangedCount = g.Select(x => x.Value).Sum()
};
OR
resultSet
.GroupBy(x => new { x.Option, x.Year })
.Select(x => new { x.Key.Option, x.Key.Year, ChangedCount = x.Select(x => x.Value).Sum() });
I have the below table which I want to group ,
Id NameId ValueId
1 1 4
1 10 18
1 9 15
2 1 4
2 10 17
2 9 0
3 1 5
3 9 16
3 10 18
4 1 5
4 10 18
4 9 16
5 1 4
5 10 17
5 9 0
The result should be grouped with Id having similar ValueId for all the corresponding NameId
Output
GroupId Id ValueId
fed1afcc-a778-48ef-9ee5-4b70886ce67c 1 4,18,15
a31055df-2e4e-472e-9301-e0e0a4e99f1e 2,5 4,17,0
8b9b3dca-4ce0-4cae-a870-1d1026bd608a 3,4 5,18,16
I actually don't need them concatenated, it is just a representation of how the output should be grouped.
I have done a working implementation using a nested for loop, which compare each entity to the subsequent entity and does a check for all the values. Am I over doing something which can be simply achieved in Linq?
Or how can this be done in Linq ?
A minimal version of my current code
var tableResult = _repository.GetData().OrderBy(x =>x.NameId).ToList();
var ids = tableResult.Select(x => x.id).Distinct().ToList();
var listOfProductGroup = new List<ProductGroup>();
for (int i = 0; i < ids.Count; i++)
{
var currentId = ids[i];
var currentEntity = tableResult.Where(x => x.id == currentId).ToList();
var productGroup = new ProductGroup(Guid.NewGuid(), currentProductId);
for (int j = i + 1; j < ids.Count; j++)
{
var subsequentId = ids[j];
var subsequentEntity = tableResult.Where(x => x.id == subsequentId ).ToList();
//This is my extension method which does the comparison
if(currentEntity.EqualsAll(subsequentEntity))
{
productGroup.Id.Add(subsequentId );
//am removing the product to avoid extra loop
ids.RemoveAt(j);
//adjust the index to match the removed product
j = j - 1;
}
}
}
listOfProductGroup.Add(productGroup);
public class ProductGroup
{
public ProductGroup(Guid groupId, int id)
{
GroupId= groupId;
Id= new List<int>()
{
id
};
}
public Guid GroupId{ get; set; }
public IList<int> Id { get; set; }
}
Something like this, perhaps:
class DictionaryComparer : IEqualityComparer<Dictionary<int, int>>
{
public bool Equals(Dictionary<int, int> x, Dictionary<int, int> y)
{
return x.Count == y.Count && !x.Except(y).Any();
}
public int GetHashCode(Dictionary<int, int> obj)
{
int hash = 0;
foreach (var kvp in obj.OrderBy(x => x.Key))
{
hash = hash ^ EqualityComparer<KeyValuePair<int, int>>.Default.GetHashCode(kvp);
}
return hash;
}
}
class Thing
{
public int Id { get; set; }
public int NameId { get; set; }
public int ValueId { get; set; }
public Thing(int id, int nameId, int valueId)
{
Id = id;
NameId = nameId;
ValueId = valueId;
}
}
class Program
{
static void Main(string[] args)
{
var data = new Thing[]
{
new Thing(1, 1, 4),
new Thing(1, 10, 18),
new Thing(1, 9, 15),
new Thing(2, 1, 4),
new Thing(2, 10, 17),
new Thing(2, 9, 0),
new Thing(3, 1, 5),
new Thing(3, 9, 16),
new Thing(3, 10, 18),
new Thing(4, 1, 5),
new Thing(4, 10, 18),
new Thing(4, 9, 16),
new Thing(5, 1, 4),
new Thing(5, 10, 17),
new Thing(5, 9, 0),
};
var #as = data.GroupBy(x => x.Id)
.Select(x => new {Id = x.Key, Data = x.ToDictionary(t => t.NameId, t => t.ValueId)})
.GroupBy(x => x.Data, x => x.Id, new DictionaryComparer());
foreach (var a in #as)
{
Console.WriteLine("{0} {1}", string.Join(",", a), string.Join(",", a.Key.Values));
}
Console.ReadKey();
}
}
This question already has answers here:
How do I get the MAX row with a GROUP BY in LINQ query?
(7 answers)
Closed 5 years ago.
This is the table "history"
id value date
1 1 01/01/2017 20:20:20
1 2 02/01/2017 20:20:20
1 3 03/01/2017 20:20:20
2 5 01/01/2017 20:20:20
2 6 02/01/2017 20:20:20
How with linq select max values for each id
context.History
.GroupBy(x => x.id) ??
.SelectOnlyWithMax(z => z.date) ??
In result only two objects
id value date
1 3 03/01/2017 20:20:20
2 6 02/01/2017 20:20:20
If you want the entire row with the highest date for each Id, you can use the following code (written with LinqPad). If you just want the Id, you can use #BurnsBA's answer, as it will be slightly more efficient.
void Main()
{
var data = new List<Record>
{
new Record(){Id=1, Value=1, Date=new DateTime(2017,1,1)},
new Record(){Id=1, Value=2, Date=new DateTime(2017,2,1)},
new Record(){Id=1, Value=3, Date=new DateTime(2017,3,1)},
new Record(){Id=2, Value=5, Date=new DateTime(2017,1,1)},
new Record(){Id=2, Value=6, Date=new DateTime(2017,2,1)},
};
var query = data.GroupBy(d => d.Id)
.SelectMany(g => g.OrderByDescending(d => d.Date)
.Take(1));
query.Dump();
}
public class Record
{
public int Id { get; set; }
public int Value { get; set; }
public DateTime Date { get; set; }
}
Results:
First it groups by Id, then sorts the items within the group by Date in descending order, and returns the first one, SelectMany then flattens the list.
public class History
{
public int id { get; set; }
public int value { get; set; }
public DateTime date { get; set; }
}
// setup:
var values = new List<History>();
values.Add(new History() { id = 1, value = 1, date = DateTime.Parse("01/01/2017 20:20:20") });
values.Add(new History() { id = 1, value = 2, date = DateTime.Parse("02/01/2017 20:20:20") });
values.Add(new History() { id = 1, value = 3, date = DateTime.Parse("03/01/2017 20:20:20") });
values.Add(new History() { id = 2, value = 5, date = DateTime.Parse("01/01/2017 20:20:20") });
values.Add(new History() { id = 2, value = 6, date = DateTime.Parse("02/01/2017 20:20:20") });
// result :
values.GroupBy(
x => x.id,
y => y.date,
// Below, dates will be enumerable
(id, dates) => new { id = id, date = dates.Max() }
)
// returns enumerable collection of anonymous type:
{
{ id = 1, date = [3/1/2017 8:20:20 PM] },
{ id = 2, date = [2/1/2017 8:20:20 PM] }
}
I suggest MoreLINQ's MaxBy function, that is:
context.History.GroupBy( x => x.id ).Select( x => x.MaxBy( y => y.date) )
I have a list to search a table,
List<long> searchListIds = new List<long>();
searchListIds.Add(1);
searchListIds.Add(2);
List<long> searchListFieldValues = new List<long>();
searchListFieldValues.Add(100);
searchListFieldValues.Add(50);
and my query is:
var adsWithRelevantadFields =
from adField in cwContext.tblAdFields
group adField by adField.adId into adAdFields
where searchListIds.All(i => adAdFields.Select(co => co.listId).Contains(i))
&& searchListFieldValues.All(i => adAdFields.Select(co => co.listFieldValue).Contains(i))
select adAdFields.Key;
everything is ok, but now: i need to get all records that meet less than searchListFieldValues. i mean:
all adId that have (listId == 1)&(listFieldValue <100) AND (listId == 2)&(listFieldValue <50)
contains part must change to something like contains-less
example:
cwContext.tblAdFields:
id 1 2 3 4 5 6 7
adId 1 2 1 2 3 3 3
listId 1 1 2 2 1 2 3
listfieldValue 100 100 50 50 100 49 10
Now if I want to get (listId == 1)&(listFieldValue ==100) AND (listId == 2)&(listFieldValue ==50) my code works, and return id adId: 1,2
but I can't get
all adId that have (listId == 1)&(listFieldValue ==100) AND (listId == 2)&(listFieldValue <50)
it must return 3
You should try changing Contains to Any, but I'm not sure if LINQ to Entities will translate it correctly into proper SQL statement.
var adsWithRelevantadFields =
from adField in cwContext.tblAdFields
group adField by adField.adId into adAdFields
where searchListIds.All(i => adAdFields.Select(co => co.listId).Contains(i))
&& searchListFieldValues.All(i => adAdFields.Select(co => co.listFieldValue).Any(x => x < i))
select adAdFields.Key;
Here is a full example that should work if I understood you correctly:
class Program
{
static void Main(string[] args)
{
List<int> searchListIds = new List<int>
{
1,
2,
};
List<int> searchListFieldValues = new List<int>
{
100,
50,
};
List<Tuple<int, int>> searchParameters = new List<Tuple<int,int>>();
for (int i = 0; i < searchListIds.Count; i++)
{
searchParameters.Add(new Tuple<int,int>(searchListIds[i], searchListFieldValues[i]));
}
List<AdField> adFields = new List<AdField>
{
new AdField(1, 1, 1, 100),
new AdField(2, 2, 1, 100),
new AdField(3, 1, 2, 50),
new AdField(4, 2, 2, 50),
new AdField(5, 3, 1, 100),
new AdField(6, 3, 2, 49),
new AdField(7, 3, 3, 10)
};
var result = adFields.Where(af => searchParameters.Any(sp => af.ListId == sp.Item1 && af.ListFieldValue < sp.Item2)).Select(af => af.AdId).Distinct();
foreach (var item in result)
{
Console.WriteLine(item);
}
Console.Read();
}
public class AdField
{
public int Id { get; private set; }
public int AdId { get; private set; }
public int ListId { get; private set; }
public int ListFieldValue { get; private set; }
public AdField(int id, int adId, int listId, int listFieldValue)
{
Id = id;
AdId = adId;
ListId = listId;
ListFieldValue = listFieldValue;
}
}
}
First, you're probably looking for functionality of Any() instead of Contains(). Another thing is that if your search criteria consists of two items - use one list of Tuple<int,int> instead of two lists. In this case you will e able to efficiently search by combination of listId and fieldValue:
var result = from adField in cwContext.tblAdFields
where searchParams.Any(sp => adField.listId == sp.Item1 && adField.listFieldValue < sp.Item2)
group adField by adField.adId into adAdFields
select adAdField.Key;
I am trying to figure out an efficient way to retrieve the data I am after. I need to get a list of all of the most recent children by ParentId coupled with all parent entries that do NOT have children. I have created a visual guide to illustrate what the response should be.
The query needs to remain as IQueryable until ALL sorting and paging is completed.
Last and LastOrDefault are not supported by LINQ to Entities (as stated by the error messages I have received while using them).
Using First or FirstOrDefault will return the error "This method or operation is not implemented"
Original Data:
-------------------------------
- Id - ParentId - CreatedDate -
-------------------------------
- 1 - - 07/01/2013 -
- 2 - - 07/01/2013 -
- 3 - - 07/01/2013 -
- 4 - 1 - 07/02/2013 -
- 5 - 2 - 07/03/2013 -
- 6 - 2 - 07/04/2013 -
- 7 - 1 - 07/05/2013 -
-------------------------------
Data returned by query
-------------------------------
- Id - ParentId - CreatedDate -
-------------------------------
- 3 - - 07/01/2013 -
- 6 - 2 - 07/04/2013 -
- 7 - 1 - 07/05/2013 -
-------------------------------
Currently, my LINQ query looks like this:
// Retrieves parent records with NO children.
var q1 = myTable
.Where(x => x.ParentId == null)
.Except(myTable
.Where(x => myTable
.Any(c => (c.ParentId == x.Id))));
// Retrieves most recent child records for each parentId
var q2 =
(from a in myTable
join b in
(myTable.Where(a => a.ParentId != null)
.GroupBy(a => a.ParentId)
.Select(b => new { ParentId = b.Key, CreatedDate = b.Max(t => t.CreatedDate) }))
on a.ParentId equals b.ParentId
where a.CreatedDate == b.CreatedDate
select a);
q1 = q1.Union(q2);
The back-end is using Npgsql2 with PostgreSQL. I am looking for a more elegant solution for this query. I am very new to LINQ and would like to optimize this.
Sorting code (sloppy, but jTable returns these strings):
if (string.IsNullOrEmpty(sorting) || sorting.Equals("Name ASC")) {
q1 = q1.OrderBy(p => p.Customer.Name);
} else if (sorting.Equals("Name DESC")) {
q1 = q1.OrderByDescending(p => p.Customer.Name);
} else if (sorting.Equals("Date ASC")) {
q1 = q1.OrderBy(p => p.CreatedDate);
} else if (sorting.Equals("Date DESC")) {
q1 = q1.OrderByDescending(p => p.CreatedDate);
}
Paging code:
var result = pageSize > 0
? q1.Skip(startIndex).Take(pageSize).ToList()
: q1.ToList();
Use grouping:
Mock data:
public class Entry {
public int Id { get; set; }
public int? ParentId { get; set; }
public DateTime Date { get; set; }
};
var list = new List<Entry> {
new Entry{ Id = 1, ParentId = null, Date = new DateTime(2013, 7, 1) },
new Entry{ Id = 2, ParentId = null, Date = new DateTime(2013, 7, 1) },
new Entry{ Id = 3, ParentId = null, Date = new DateTime(2013, 7, 1) },
new Entry{ Id = 4, ParentId = 1, Date = new DateTime(2013, 7, 2) },
new Entry{ Id = 5, ParentId = 2, Date = new DateTime(2013, 7, 3) },
new Entry{ Id = 6, ParentId = 2, Date = new DateTime(2013, 7, 4) },
new Entry{ Id = 7, ParentId = 1, Date = new DateTime(2013, 7, 5) }
};
Query:
var query = from l in list
group l by l.ParentId into g
select new {
Items = g.OrderBy(x => x.Date).Last()
};
var res = query.OrderBy(x => x.Items.Id).Select(x => x.Items).ToList();
LinqPad result:
Id ParentId Date
3 null 01.07.2013 0:00:00
6 2 04.07.2013 0:00:00
7 1 05.07.2013 0:00:00
I can propose a different query, still in two phases
var firstQuery = myTable.Select(p => new { p.ID, ParentID = p.ParentID ?? p.ID, p.CreatedDate })
.GroupBy( p => p.ParentID).Select( q => new
{
el = q.OrderByDescending( k => k.CreatedDate).Take(1)
}).SelectMany(t => t.el);
var result = dc.TabellaId_ParentId.Where(p => test.Select(q => q.ID).Contains(p.ID));