Multiple Aggregation Levels in Linq - c#

I am not sure what my issue to be called, although I mentioned in the subject as "Multiple Aggregation Levels".
I would like to Aggregate different dimensions of the data presented. In this Example, I am trying to get Aggregation data by SalesCode and in Detail Aggregation by AccountId. So basically, I could get the Accounts that were associated to the Sales Aggregation Level.
Hence the output I am ought to get should be like this:
My Requirement is to Map the data to the following Class:
public class Earning
{
public string EntityId
{
get;
set;
}
public string EntityName
{
get;
set;
}
public string EntityType
{
get;
set;
}
public int TradeCount
{
get;
set;
}
public int OrderCount
{
get;
set;
}
public decimal PrincipalAmount
{
get;
set;
}
public decimal GrossBrokerage
{
get;
set;
}
public decimal NetBrokerage
{
get;
set;
}
public List<Earning> Detail
{
get;
set;
}
}
From the following data:
List<Trade> Trades = new List<Trade>(){
new Trade{
AccountId = "ACT01",
SalesCode = "STEVES",
PrincipalAmount = 100,
GrossBrokerage = 0.64M,
NetBrokerage = 0.64M
},
new Trade{
AccountId = "ACT02",
SalesCode = "STEVES",
PrincipalAmount = 100,
GrossBrokerage = 0.64M,
NetBrokerage = 0.64M
},
new Trade{
AccountId = "ACT01",
SalesCode = "STEVES",
PrincipalAmount = 50,
GrossBrokerage = 0.32M,
NetBrokerage = 0.32M
},
new Trade{
AccountId = "ACT03",
SalesCode = "GRAHAMS",
PrincipalAmount = 100,
GrossBrokerage = 0.64M,
NetBrokerage = 0.64M
},
};
Until now I have tried the following ways in the working I am mentioning below, but I am clueless to get the 2nd Level aggregation, which is by AccountId.
DotNetFiddle: https://dotnetfiddle.net/SxGdDD
The Trade Class look like this:
public class Trade
{
public string AccountId
{
get;
set;
}
public string SalesCode
{
get;
set;
}
public decimal PrincipalAmount
{
get;
set;
}
public decimal GrossBrokerage
{
get;
set;
}
public decimal NetBrokerage
{
get;
set;
}
}

You need another GroupBy.
var results = (
from r in Trades
group r by r.SalesCode
into g
select new Earning()
{
EntityId = g.Key.ToString(),
EntityName = g.Key.ToString(),
TradeCount = g.Count(),
OrderCount = g.Count(),
PrincipalAmount = g.Sum(c => c.PrincipalAmount),
GrossBrokerage = g.Sum(c => c.GrossBrokerage),
NetBrokerage = g.Sum(c => c.NetBrokerage),
Detail = g.GroupBy(c=>c.AccountId).Select(c => new Earning()
// Added GroupBy ---^^
{
EntityId = c.Key,
EntityName = c.Key,
TradeCount = c.Count(),
OrderCount = c.Count(),
PrincipalAmount = c.Sum(p=>p.PrincipalAmount),
GrossBrokerage = c.Sum(p=>p.GrossBrokerage),
NetBrokerage = c.Sum(p=>p.NetBrokerage),
}).ToList(),
}).ToList();
foreach (var item in results)
{
Console.WriteLine(item.EntityId);
Console.WriteLine(string.Format("Total Principal Amount: {0}", item.PrincipalAmount.ToString()));
Console.WriteLine(string.Format("Total Gross Brokerage Amount: {0}", item.GrossBrokerage.ToString()));
Console.WriteLine(string.Format("Total Net Brokerage Amount: {0}", item.NetBrokerage.ToString()));
foreach (Earning detail in item.Detail)
{
Console.WriteLine(string.Format("-- Detail {0}/{1}", detail.EntityId, detail.EntityName));
}
}

Related

Filter data from 2 lists with diferent models C#

I have this models
public class RoutingAttributeModel
{
public int Bus_No { get; set; }
public int Attribute_No { get; set; }
public string Attribute_Name { get; set; }
public string Status { get; set; }
public string Notes { get; set; }
}
public class AgentRoutingAttributeModel
{
public int Agent_No { get; set; }
public int Bus_No { get; set; }
public int Attribute_No { get; set; }
public string Attribute_Name { get; set; }
public string Status { get; set; }
}
List<RoutingAttributeModel> lstComplete = new List<RoutingAttributeModel>();
List<AgentRoutingAttributeModel> lstAssigned = new List<AgentRoutingAttributeModel>();
Filled this with some data
Is it possible to filter with Linq? I want to save in a new list the diferent content between lstComplete and lstAssigned
I was trying to join both lists but got stuck there
var results1 = from cl in lstComplete
join al in lstAssigned
on cl.Attribute_No equals al.Attribute_No
select cl;
you can use linq
as my understanding, you try to find linked by attribute_No records and have a list of not matching properties?
lstComplete.Add(new RoutingAttributeModel(){
Attribute_Name = "aaa",
Attribute_No = 1,
Bus_No = 1,
Notes = "",
Status = "status"
});
lstAssigned.Add(new AgentRoutingAttributeModel()
{
Attribute_No = 1,
Agent_No = 10,
Bus_No = 1,
Attribute_Name = "bbb",
Status = "status2"
});
var lst = lstComplete
.Join(lstAssigned,
complete => complete.Attribute_No,
assigned => assigned.Attribute_No,
(complete, assigned) => new { lstComplete = complete, lstAssigned = assigned })
.Select(s => new { s.lstComplete, s.lstAssigned})
.Where(w=>
w.lstAssigned.Attribute_Name != w.lstComplete.Attribute_Name
|| w.lstAssigned.Bus_No != w.lstComplete.Bus_No
)
.ToList()
.Dump();
so result would be
You could try the following query
var filteredList = lstComplete
.Where(x => !lstAssigned.Any(y => y.Attribute_No == x.Attribute_No));

Extracting Items over an List<T> of List<T>

I am trying to retrieve records from a List<T> of List<T> and seek your help in getting it.
I am trying to fetch items where overdues.Accounts.AccountId = 'JKB1' and how can i do it over the below List Items.
public class OverdueModel
{
public string Slab { get; set; }
public double Value { get; set; }
public double Percentage { get; set; }
public List<OverdueSlabAccounts> Accounts { get; set; }
}
public class OverdueSlabAccounts
{
public string AccountId { get; set; }
public string AccountName { get; set; }
public string SalesCode { get; set; }
public string Value { get; set; }
}
void Main(){
List<OverdueModel> overdues = new List<OverdueModel>();
List<OverdueSlabAccounts> accounts = new List<OverdueSlabAccounts>();
//For T3
accounts.Clear();
accounts.Add(new OverdueSlabAccounts()
{
AccountId = "JKB1",
AccountName = "JKB1",
SalesCode = "JKB",
Value = "500"
});
accounts.Add(new OverdueSlabAccounts()
{
AccountId = "JKB2",
AccountName = "JKB2",
SalesCode = "JKB",
Value = "500"
});
overdues.Add(new OverdueModel()
{
Slab = "T3",
Value = 1000,
Percentage = 0,
Accounts = accounts
});
//For T4
accounts.Clear();
accounts.Add(new OverdueSlabAccounts()
{
AccountId = "JKB1",
AccountName = "JKB1",
SalesCode = "JKB",
Value = "1000"
});
overdues.Add(new OverdueModel()
{
Slab = "T4",
Value = 1000,
Percentage = 0,
Accounts = accounts
});
}
You can use Where and Any in combination for this :
var result = overdues
.Where(overdue => overdue.Accounts
.Any(account => account.AccountId == "JKB1"));
This will filter those overdues for which associated any Account has AccountId JKB1
You could use Linq for the purpose
var filteredList = overdues.Where(x=>x.Accounts.Any(c=>c.AccountId=="JKB1"));
For more information on Where and Any
Enumerable.Where : Refer
Enumerable.Any : Refer
Output
You can try this:
var account = accounts.Find(x => x.AccountId.Contains("JKB1")));
or
var account = accounts.Find(x => x.AccountId.Equals("JKB1")));
this will get you the specific account Id you are looking for.

Issue getting different types value of self join table

I have a self join table of population. Population is entered at village level and it should be automatically calculated on Union council(UC), Tehsil and District Level.
I am using .net MVC in this application. Following is my code
enum of population type
public enum UnitType
{
Village,
UC,
Tehsil,
Dist
}
population structure, here names of villages, UC, Tehsil and district are added
public class Village
{
public int Id { get; set; }
public string Name { get; set; }
public UnitType UnitType { get; set; }
public int? ParientId { get; set; }
public Village Parient { get; set; }
}
Enter population at village level
public class Population
{
public int Id { get; set; }
public int VillageId { get; set; }
public Village Village { get; set; }
public int NoOfPerson { get; set; }
}
I need the following output result. I can get the village level population but i am confused in getting related totals. Its looks very simple but i think i am not going in right direction.
POPULATION
Code Name Type Population
1 Chakwal Disttrict 20000 (total population of all tehsils)
2 Choa Tehsil 20000 (Tehsil total of two Union Councils)
3 Dalwal UC 14300 UC is total of village population
4 Waulah Village 9800
5 DalPur VIllage 4500
Dulmial UC 5700 UC is total of village population
Tatral Village 3400
Arar Village 2300
You need to join the two classes :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
List<Village> villages = new List<Village>() {
new Village() { Id = 1, Name = "Chakwal", UnitType = UnitType.Dist},
new Village() { Id = 2, Name = "Choa", UnitType = UnitType.Tehsil},
new Village() { Id = 3, Name = "Dalwal", UnitType = UnitType.UC},
new Village() { Id = 4, Name = "Waulah", UnitType = UnitType.Village},
new Village() { Id = 5, Name = "DalPur", UnitType = UnitType.Village},
new Village() { Id = 6, Name = "Dulmial", UnitType = UnitType.UC},
new Village() { Id = 7, Name = "Tatral", UnitType = UnitType.Village},
new Village() { Id = 8, Name = "Arar", UnitType = UnitType.Village}
};
List<Population> populations = new List<Population>() {
new Population() { Id = 1, NoOfPerson = 20000},
new Population() { Id = 2, NoOfPerson = 20000},
new Population() { Id = 3, NoOfPerson = 14300},
new Population() { Id = 4, NoOfPerson = 9800},
new Population() { Id = 5, NoOfPerson = 4500},
new Population() { Id = 6, NoOfPerson = 5700},
new Population() { Id = 7, NoOfPerson = 3400},
new Population() { Id = 8, NoOfPerson = 2300}
};
var results = (from v in villages
join p in populations on v.Id equals p.Id
select new { v = v, p = p }
).ToList();
StreamWriter writer = new StreamWriter(FILENAME);
writer.WriteLine("{0,25}","POPULATION");
writer.WriteLine("{0,-5}{1,-8}{2,-14}{3,-10}", "Code", "Name", "Type", "Population");
foreach (var result in results)
{
writer.WriteLine("{0,-5}{1,-8}{2,-14}{3,-10}", result.v.Id.ToString(), result.v.Name, result.v.UnitType.ToString(), result.p.NoOfPerson.ToString());
}
writer.Flush();
writer.Close();
}
}
public enum UnitType
{
Village,
UC,
Tehsil,
Dist
}
public class Village
{
public int Id { get; set; }
public string Name { get; set; }
public UnitType UnitType { get; set; }
public int? ParientId { get; set; }
public Village Parient { get; set; }
}
public class Population
{
public int Id { get; set; }
public int VillageId { get; set; }
public Village Village { get; set; }
public int NoOfPerson { get; set; }
}
}
Here is the answer for the question
var villages = db.Populations.Include(l => l.Village).ToList();
var ucpop = villages.GroupBy(l => l.UCId).Select(g=> new {
ucId = g.Key,
UcName = db.Villages.Find(g.Key),
VillageCount = g.Count(),
UCPop = g.Sum(l=>l.NoOfPerson),
villages = g.Where(l=>l.Village.UnitType == UnitType.Village).ToList()
}).ToList();
var tehpop = ucpop.GroupBy(l => l.UcName.ParientId).Select(g => new
{
tehId = g.Key,
tehName = db.Villages.Find(g.Key),
tehCount = g.Count(),
tehPop = g.Sum(l => l.UCPop),
uclist = g.Where(l=>l.UcName.UnitType == UnitType.UC).ToList()
}).ToList();
var distpop = tehpop.GroupBy(l => l.tehName.ParientId).Select(g => new
{
distId = g.Key,
distName = db.Villages.Find(g.Key),
distCount = g.Count(),
distPop = g.Sum(l => l.tehPop),
tehlist = g.Where(l => l.tehName.UnitType == UnitType.Tehsil).ToList()
}).ToList();

Find Unique count on field using LINQ

I am trying to determine the Distinct count for a particular field in a collection of objects.
private static RemittanceCenterBatchSummaryListModel SummarizeFields(RemittanceCenterSummaryListModel remittanceCenterSummaryListModel)
{
var result = remittanceCenterSummaryListModel.RemittanceBatchSummaryRecord.GroupBy(x => new{x.FileId, x.SourceFileName, x.BatchCode, x.BatchType})
.Select(x => new RemittanceCenterBatchSummarizedModel()
{
FileId = x.Key.FileId,
SourceFileName = x.Key.SourceFileName,
BatchCode = x.Key.BatchCode,
BatchType = x.Key.BatchType,
DetailRecordCountAdc = x.Count(y => y.BillingSystemCode == BillingSystemCode.Adc),
DetailRecordCountNotAdc = x.Count(y => y.BillingSystemCode == BillingSystemCode.Exd),
AmountAdc = x.Where(y => y.BillingSystemCode == BillingSystemCode.Adc).Sum(y => y.PaymentAmount),
AmountNotAdc = x.Where(y => y.BillingSystemCode == BillingSystemCode.Exd).Sum(y => y.PaymentAmount),
UniqueFileCount = x.Select(y => x.Key.FileId).Distinct().Count()
});
return CreateSummaryListModel(result);
}
Input entities:
public class RemittanceCenterSummaryListModel
{
public RemittanceCenterSummaryListModel()
{
this.RemittanceBatchSummaryRecord = new List<RemittanceBatchProcessingModel>();
}
public List<RemittanceBatchProcessingModel> RemittanceBatchSummaryRecord { get; private set; }
}
public class RemittanceCenterBatchSummarizedModel
{
public string FileId { get; set; }
public string SourceFileName { get; set; }
public string BatchCode { get; set; }
public string BatchType { get; set; }
public int DetailRecordCountAdc { get; set; }
public int DetailRecordCountNotAdc { get; set; }
public int DetailRecordCountTotal { get; set; }
public decimal AmountAdc { get; set; }
public decimal AmountNotAdc { get; set; }
public decimal AmountTotal { get; set; }
public BillingSystemCode BillingSystemCode { get; set; }
public int UniqueFileCount { get; set; }
}
private static RemittanceCenterBatchSummaryListModel CreateSummaryListModel(IEnumerable<RemittanceCenterBatchSummarizedModel> summaryModels)
{
var summaryModelList = new RemittanceCenterBatchSummaryListModel();
foreach (var summaryRec in summaryModels)
{
var summaryModel = new RemittanceCenterBatchSummarizedModel
{
FileId = summaryRec.FileId,
SourceFileName = summaryRec.SourceFileName,
BatchCode = summaryRec.BatchCode,
BatchType = summaryRec.BatchType,
DetailRecordCountAdc = summaryRec.DetailRecordCountAdc,
DetailRecordCountNotAdc = summaryRec.DetailRecordCountNotAdc,
AmountAdc = summaryRec.AmountAdc,
AmountNotAdc = summaryRec.AmountNotAdc,
UniqueFileCount = summaryRec.UniqueFileCount
};
summaryModelList.RemittanceBatchSummary.Add(summaryModel);
}
return summaryModelList;
}
Example input records:
Record1:
FileId: '123'
SourceFileName: 'test.file.txt'
BatchCode: 'aaa'
BatchType: 'scanned'
PaymentAmount: '50.00'
BillingSystemCode: 'Adc'
Record1:
FileId: '1234'
SourceFileName: 'test.file2.txt'
BatchCode: 'aab'
BatchType: 'scanned'
PaymentAmount: '52.00'
BillingSystemCode: 'Adc'
ActualOuput for UniqueFileCount Field:
UniqueFileCount = 1
ExpectedOutput results for UniqueFileCount Field:
UniqueFileCount = 2
What am I doing wrong?
It sounds like you want the distinct count of FileId for the entire collection and not just for each group, which will always be 1 since FileId is one of the fields you group on. If that is the case then you can just calculate that count first
int distinctFileIds = remittanceCenterSummaryListModel.RemittanceBatchSummaryRecor‌​d
.Select(x => x.FileId)
.Distinct()
.Count();
Then use that in your Linq query
UniqueFileCount = distinctFileIds

Join with inner list

I have this linq query:
var investorData = from investor in db.Investors
join investorLine in db.InvestorStatementLines
on investor.InvestorID equals investorLine.InvestorID
where investor.UserId == userId
select new InvestorViewModel()
{
InvestorId = investor.InvestorID,
InvestorName = investor.Name,
FundingDate = investor.FundingDate,
DueDate = investor.DueDate,
FundsCommitted = investor.FundsCommitted,
FundsInvested = investor.FundsInvested,
StatementLines =
db.InvestorStatementLines.Where(s => s.InvestorID == investor.InvestorID)
.Select(t => new InvestorStatementLineVM
{
Balance = t.Balance,
Credit = t.Credit,
Debit = t.Debit,
InvestorStatementLineDetails = t.Details,
Date = t.Date
}).ToList()
};
The viewmodel:
public class InvestorViewModel
{
public int InvestorId { get; set; }
public string InvestorName { get; set; }
public DateTime FundingDate { get; set; }
public DateTime? DueDate { get; set; }
public Decimal? FundsCommitted { get; set; }
public Decimal? FundsInvested { get; set; }
public List<InvestorStatementLineVM> StatementLines { get; set; }
}
What is happening is once I'm executing the query I'm getting 125 records, and that's the number of the StatementLines for that investor. So I'm getting 125 same records but I'm expecting one result which will have 125 statement lines in the inner list.
Is this query correct?
This is how you can do that with navigation properties
var investorData = from investor in db.Investors
where investor.UserId == userId
select new InvestorViewModel()
{
InvestorId = investor.InvestorID,
InvestorName = investor.Name,
FundingDate = investor.FundingDate,
DueDate = investor.DueDate,
FundsCommitted = investor.FundsCommitted,
FundsInvested = investor.FundsInvested,
StatementLines = investor.InvestorStatementLines
.Select(t => new InvestorStatementLineVM
{
Balance = t.Balance,
Credit = t.Credit,
Debit = t.Debit,
InvestorStatementLineDetails = t.Details,
Date = t.Date
}).ToList()
};
Use GroupJoin instead of Join: (_join x in y on x.a equals y.a
into z_)
var investorData = from investor in db.Investors
join investorLine in db.InvestorStatementLines
on investor.InvestorID equals investorLine.InvestorID
into investorLine
where investor.UserId == userId
select new InvestorViewModel()
{
InvestorId = investor.InvestorID,
InvestorName = investor.Name,
FundingDate = investor.FundingDate,
DueDate = investor.DueDate,
FundsCommitted = investor.FundsCommitted,
FundsInvested = investor.FundsInvested,
StatementLines = investorLine
.Select(t => new InvestorStatementLineVM
{
Balance = t.Balance,
Credit = t.Credit,
Debit = t.Debit,
InvestorStatementLineDetails = t.Details,
Date = t.Date
}).ToList()
};
Also instead of performing the sub-query just use the data from the join you just performed.
A better option, using entity framework, is using navigation properties and then you do not need to perform a join but you just have
InvestorStatementLines as a property of your investor.
To set the navigation properties:
public class InvestorViewModel
{
public int InvestorId { get; set; }
public string InvestorName { get; set; }
public DateTime FundingDate { get; set; }
public DateTime? DueDate { get; set; }
public Decimal? FundsCommitted { get; set; }
public Decimal? FundsInvested { get; set; }
public virtual ICollection<InvestorStatementLineVM> StatementLines { get; set; }
}
And the query will be as simple as:
var investorData = from investor in db.Investors
where investor.UserId == userId
select new InvestorViewModel()
{
InvestorId = investor.InvestorID,
....
StatementLines = investor.InvestorStatementLines.Select(....)
};

Categories