what I am trying to achieve here is that I want from that LINQ query to return the list with two poperties: billNo, and number of occurences of the importcode on the same fromDate.
So here we have a billNo 1 and 2 both have the same importcode which appears in two rows on the same date (01/01/2020) thus count is 2.
If it helps to clarify, think of it as import code should only cover one distinct fromDate. If it appears multiple time, I would like to see how many times (count) and which BillNo s are like that.
So expected result for dataset below would be:
BillNo
Count
1
2
2
2
3
1
4
1
5
1
I struggle to figure out how to select BillNo if it is not used for grouping.
Thanks a lot for helping.
var rows = new List<ImportRow>()
{
new ImportRow {billNo= 1, importCode = "one", fromDate = new DateTime(2020, 1, 1)},
new ImportRow {billNo= 2, importCode = "one", fromDate = new DateTime(2020, 1, 1)},
new ImportRow {billNo= 3, importCode = "two", fromDate = new DateTime(2020, 1, 1)},
new ImportRow {billNo= 4, importCode = "two", fromDate = new DateTime(2020, 2, 1)},
new ImportRow {billNo= 5, importCode = "one", fromDate = new DateTime(2020, 3, 1)}
};
public class ImportRow : IEnumerable
{
public int billNo { get; set; }
public string importCode { get; set; }
public DateTime fromDate { get; set; }
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
var billNosWithCounts = rows.GroupBy(info => new { info.importCode,
info.fromDate })
.Select(group => new
{
FromDate = group.Key.fromDate,
Count = group.Count()
});
You can join the source onto itself, I believe that will give you the output you are looking for:
var result = from r1 in rows
join r2 in rows
on new { r1.importCode, r1.fromDate } equals
new { r2.importCode, r2.fromDate }
group r1.billNo by r1.billNo into g
select new
{
BillNo = g.Key,
Count = g.Count()
};
You could potentially achieve what you are looking for by doing the following:
public static IDictionary<int, string> Map(this List<Sample> collection)
{
var kvp = new Dictionary<int, string>();
var records = collection.GroupBy(value => new { value.Code, value.Date });
foreach(var record in records)
foreach(var contents in record)
kvp.Add(contents.Bill, contents.Count());
}
A sample of the object you outlined above:
var collection = new List<Sample>()
{
new Sample() { Bill = 1, Code = "ABC", Date = DateTime.Now.ToShortDateString() },
new Sample() { Bill = 2, Code = "ABC", Date = DateTime.Now.ToShortDateString() },
new Sample() { Bill = 3, Code = "ABCD", Date = DateTime.Now.ToShortDateString() },
new Sample() { Bill = 4, Code = "ABCDE", Date = DateTime.Now.AddDays(-3).ToShortDateString() }
};
In essence, I created a method that will let you pass an object. It will then group on the import code and the date. Creating the unique pair you desire. Then I simply add the newly transformed content into a Dictionary. You could use Linq to do the full transform, but it might become more difficult to read or understand, so I chose to simplify into basic loops for the transformation.
Related
I have a class like
class MyClass
{
public DateTime Date { get; set; }
public List<int> IdList { get; set; }
public MyClass(DateTime initDate)
{
Date = initDate;
IdList = new List<int>();
}
}
and need to count the number of entries in a List<MyClass>, grouped by each int in IdList.
I have experimented with various Linq constructs, but I cannot get anything to work. Here is what I have so far:
List<MyClass> myc = new List<MyClass>();
myc.Add(new MyClass(new DateTime(2016, 1, 1)) { IdList = new List<int> { 1, 2 } });
myc.Add(new MyClass(new DateTime(2016, 1, 2)) { IdList = new List<int> { 1, 3 } });
myc.Add(new MyClass(new DateTime(2016, 1, 3)) { IdList = new List<int> { 1, 4 } });
myc.Add(new MyClass(new DateTime(2016, 1, 4)) { IdList = new List<int> { 5, 6 } });
myc.Add(new MyClass(new DateTime(2016, 1, 5)) { IdList = new List<int> { 2, 3 } });
var grouped = from p in myc
group p by p.IdList into g
select new { Id = g.Key, Count = g.Count() };
foreach (var x in grouped)
{
Console.WriteLine("ID: {0}, Count: {1}", x.Id, x.Count);
}
// Expecting output like:
// ID: 1, Count: 3
// ID: 2, Count : 2
// etc.
If there was a single int Id property in MyClass, it would be straightforward, but I cannot work out how to use the List<int>. Is there any alternative to writing nested loops and populating a Dictionary? Thanks for any help.
You can use SelectMany
var grouped = myc.SelectMany(x => x.IdList).GroupBy(x => x);
foreach (var i in g)
{
Console.WriteLine(string.Format("Id: {0}, Count: {1}", i.Key,i.Count()));
}
This should give you the output you're looking for.
I don't know if I've understand your requeriment correctly. But try this and let me know:
var groupedIds = myc.SelectMany(x => x.IdList.Select(i => i))
.GroupBy(x => x)
.ToList();
The full fiddle here
And here SelectMany documentation so you know what this code means.
Hope this helps!
I have a list of objects in which every object is containing a list itself. how do I get the the JellyFishID field or the Amount field for using an IF argument
(I'm currently using Foreach):`
public static List<Report> DataSorted = new List<Report> {
new Report() { IsGoldUser=true, Date=new DateTime(2016, 3, 12,11, 59, 33), IsBurningWater=true, Type=Type.Shore, ZoneID = 1 ,
ReportDetails =new List<ReportDetail> { new ReportDetail() { Amount = Amount.Few, Jellyfish = new Jellyfish { JellyfishID = 1, Venom = Venom.Strong } } } },
new Report() { IsGoldUser=true, Date=new DateTime(2016, 3, 12, 11, 59, 33), IsBurningWater=true, Type=Type.Shore, ZoneID = 1 ,
ReportDetails =new List<ReportDetail> { new ReportDetail() { Amount = Amount.Few, Jellyfish = new Jellyfish { JellyfishID = 1, Venom = Venom.Strong } } } },
new Report() { IsGoldUser=true, Date=new DateTime(2016, 3, 12, 11, 59, 33), IsBurningWater=true, Type=Type.Shore, ZoneID = 1 ,
ReportDetails =new List<ReportDetail> { new ReportDetail() { Amount = Amount.Few, Jellyfish = new Jellyfish { JellyfishID = 1, Venom = Venom.Strong } } } },
new Report() { IsGoldUser=true, Date=new DateTime(2016, 3, 12, 11, 59, 33), IsBurningWater=true, Type=Type.Shore, ZoneID = 1 ,
ReportDetails =new List<ReportDetail> { new ReportDetail() { Amount = Amount.Few, Jellyfish = new Jellyfish { JellyfishID = 1, Venom = Venom.Strong } } } },
foreach (var item in DataSorted)
{
if (item.ReportDetails....) //???I want here to Make an Argument about The Amount field or the JellyFishID field in the list above....
}
You don't describe exactly what you want to check, but with LINQ to Objects you have a lot of possiblities. At first, you need to reference the correct namespace with
using System.Linq;
at the top of your source code file.
Now, if you want to check if any items of your list contains a jellyfish with a given ID, you can use:
if (item.ReportDetails.Any(t => t.Jellyfish.JellyfishID == 1)) //...
Additionally you can have conditions inside a Where-function to filter your list and search only for jellyfish with a few amount:
if (item.ReportDetails.Where(t => t.Amount == Amount.Few).
Any(t => t.Jellyfish.JellyfishID == 1)) //...
There is a lot of information avaliable about LINQ, a lot of examples are in the MSDN (for example this intro page), but there are alternatives like this one: 101 Linq examples. It even has a tag on StackOverflow.
I have a LINQ query something like below:
var results=from p in inputTable.AsEnumerable()
where inputParameter.Contains(p.Field<String>(inputField))&&
p.Field<string>(CurrencyCode)==currencyCode
group p by new
{
p[CurrencyCode],
} into groupedTable
select new
{
Amount = groupedTable.Sum(r => r.Field<System.Decimal>(amountField))
};
if (results.Count() > 0)
{
retVal = results.ElementAt(0).Amount;
}
My inputParameter is basically a List<string> which will have values like {October, November, December}.
The inputField is November.
My thought is that since the where condition has a Contains method it will just filter rows by November, since inputField is November.
I basically need to pass all the elements of the list, i.e. October, November and December and then get records filtered by these months.
I tried to use where-in stuff of LINQ but was not successful.
Experts please help to crack this question.
Any help/pointer will be highly appreciable.
EDIT:
Let me try to make this question very simple.
My List<string> inputParameter can contain variable strings, like {October, November, December} or {January, February, March, April} and so on.
I need my query to pass in all these values and filter the records accordingly.
The simplified query which I tried is follows:
var results=from p in inputTable.AsEnumerable()
where p.Field<string>(FiscalMonth)==inputParameter[0] ||
p.Field<string>(FiscalMonth)==inputParameter[1] ||
p.Field<string>(FiscalMonth)==inputParameter[2]
select new
{
p.Amount
};
In the above instance I have basically hardcoded the individual elements of the List inputParameter, but my list will be variable at times. i.e it may hold 3 items, 4 items, or even 12 items.
How to mould the above query to avoid individual hard-coding?
Regards
Anurag
Responding to your edit, it should be :
var results = from p in inputTable.AsEnumerable()
where inputParameter.Contains(p.Field<string>(FiscalMonth))
select new
{
p.Amount
};
This is a working console example that demonstrate query with Contains :
//create datatable with column FiscalMonth
var table = new DataTable();
table.Columns.Add("FiscalMonth");
//add two rows, January and October
var row1 = table.NewRow();
row1["FiscalMonth"] = "January";
var row2 = table.NewRow();
row2["FiscalMonth"] = "October";
table.Rows.Add(row1);
table.Rows.Add(row2);
//query from data table where FiscalMonth in (October, November, December)
var inputParameter = new List<string> {"October", "November", "December"};
var result = from r in table.AsEnumerable()
where inputParameter.Contains(r.Field<string>("FiscalMonth"))
select r.Field<string>("FiscalMonth");
//the result will be only one row, which is October. January is successfully filtered out
foreach (var r in result)
{
Console.WriteLine(r);
}
Do not really understand exactly what you are looking for, since you are stating that you want to query on only Month and your code is querying on month and currency code. but looks like you are returning the sum of the Amount. So he is a first stab at what you want to do.
class MonthCurrency
{
public string Month { get; set; }
public int CurrencyCode { get; set; }
public decimal Amount { get; set; }
}
static List<MonthCurrency> inputTable = null;
static void Main(string[] args){
inputTable = new List<MonthCurrency>()
{ new MonthCurrency() { Month = "October", CurrencyCode= 1, Amount = 1},
new MonthCurrency() { Month = "October", CurrencyCode= 1, Amount = 2},
new MonthCurrency() { Month = "November", CurrencyCode= 2, Amount = 1},
new MonthCurrency() { Month = "November", CurrencyCode= 2, Amount = 2},
new MonthCurrency() { Month = "December", CurrencyCode= 3, Amount = 1},
new MonthCurrency() { Month = "December", CurrencyCode= 3, Amount = 2},
};
var result = GetCurrencyCode("November");
}
static public decimal GetCurrencyCode(string inputParameter)
{
decimal retVal = 0.0M;
var results = from p in inputTable.AsEnumerable()
where p.Month == inputParameter
group p by new
{
p.CurrencyCode,
} into groupedTable
select new MonthCurrency
{
Amount = groupedTable.Sum(r => r.Amount)
};
if (results.Count() > 0)
{
retVal = results.ElementAt(0).Amount;
}
return retVal;
}
Hopefully this will help you out
I have 2 lists in C#:
public class AvailableSlot
{
public DateTime DateTime;
public string Name
}
List<AvailableSlot> list1 = GetList();
List<AvailableSlot> list2 = GetAnotherList();
I want to call intersect on these lists to find out where there are items in both lists for the same date. I know i can use .Intersect to get this info but I have a slightly more complicated requirement. I want to return a intersected list but i want to this list to contain a list of objects with all of the name in them. so something like this:
List<AvailableSlot2> intersectedList . ..
where AvailableSlot2 is this below:
public class AvailableSlot2
{
public DateTime DateTime;
public string[] Names;
}
Is there any ability to do this transformation after trying to intersect between two lists?
I would just union the two lists, group by DateTime and then pull out the names from the group:
var list1 = new List<AvailableSlot>
{
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Alpha" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Bravo" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 3), Name = "Charlie" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Delta" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Echo" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 3), Name = "Foxtrot" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 4), Name = "Golf" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 5), Name = "Hotel" }
};
var list2 = new List<AvailableSlot>
{
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Apple" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Bannana" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Dog" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Egg" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 5), Name = "Hi" }
};
var list3 = list1.Where (l => list2.Where (li => l.DateTime == li.DateTime).Any ())
.Union(list2.Where (l => list1.Where (li => l.DateTime == li.DateTime).Any ()));
var groupedItems = from slot in list3
group slot by slot.DateTime into grp
select new AvailableSlot2 {
DateTime = grp.Key,
Names = grp.Select (g => g.Name).ToArray()
};
foreach(var g in groupedItems)
{
Console.WriteLine(g.DateTime);
foreach(var name in g.Names)
Console.WriteLine(name);
Console.WriteLine("---------------------");
}
Output:
2/1/2013 12:00:00 AM
Alpha
Delta
Apple
Dog
---------------------
2/2/2013 12:00:00 AM
Bravo
Echo
Bannana
Egg
---------------------
2/5/2013 12:00:00 AM
Hotel
Hi
---------------------
You can use a LINQ to Objects Join() to line up items with the same DateTime property and then collect all the names into an array
var joinedItems = from slot1 in list1
join slot2 in list2
on slot1.DateTime equals slot2.DateTime into g
where g.Any()
select new AvailableSlot2
{
DateTime = slot1.DateTime,
Names = Enumerable.Range(slot1.Name,1).Union(g.Select(s => s.Name)).ToArray()
}
You can make use of ToLookup:
DateTime dt1 = new DateTime(2013, 2, 1);
DateTime dt2 = new DateTime(2013, 3, 1);
DateTime dt3 = new DateTime(2013, 4, 1);
var list1 = new List<AvailableSlot>
{
new AvailableSlot{DateTime = dt1, Name = "n1",},
new AvailableSlot{DateTime = dt2, Name = "n2",},
new AvailableSlot{DateTime = dt1, Name = "n3",},
};
var list2 = new List<AvailableSlot>
{
new AvailableSlot{DateTime = dt1, Name = "n1",},
new AvailableSlot{DateTime = dt2, Name = "n2",},
new AvailableSlot{DateTime = dt3, Name = "n3",},
};
var intersected = list1.Select (l => l.DateTime).
Intersect(list2.Select (l => l.DateTime));
var lookup = list1.Union(list2).ToLookup (
slot => slot.DateTime, slot => slot);
lookup.Where (l => intersected.Contains(l.Key)).Select (
slot => new
{
DateTime=slot.Key,
Names=slot.Select (s => s.Name)
});
Which in this case gives the result:
DateTime Names
01/02/2013 00:00 n1
n3
n1
01/03/2013 00:00 n2
n2
You could of course use Names=slot.Select(s => s.Name).Distinct() to get a distinct list of names.
Given this example data (in .NET classes where Po, Sku, Qty are properties):
PO, Sku, Qty
1,ABC,1
1,DEF,2
1,GHI,1
1,QWE,1
1,ASD,1
1,ZXC,5
1,ERT,1
2,QWE,1
2,ASD,11
2,ZXC,1
3,ERT,1
3,DFG,1
3,DFH,1
3,CVB,4
3,VBN,1
3,NMY,1
I need to transform it into a fixed column format, with a max of 5 SKUs per line (repeating the PO if needed for > 5):
PO, SkuA, QtyA, SkuB, QtyB, SkuC, QtyC, SkuD, QtyD, SkuE, QtyE
1, ABC, 1, DEF, 2, GHI, 1, QWE, 1, ASD, 1
1, ZXC, 5, ERT, 1, , , , , ,
2, QWE, 1, ASD, 11, ZXC, 1, , , ,
3, ERT, 1, DFG, 1, DFH, 1, CVB, 4, VBN, 1
3, NMY, 1, , , , , , , ,
Output can be CSV (which is what I'm outputting), or .NET classes - no matter there. Is there a simple way to do this in Linq by grouping by PO, then by counts of 5?
EDIT: I have no control of over the destination format. And for anyone interested, it's VendorNet and VendorBridge that require this nonsense.
Firstly, here's the query that will generate the correct hierarchy of objects. I'm using anonymous types but it's easy enough to change it to use your own proper classes.
var query = yourData
.GroupBy
(
x => x.PO
)
.SelectMany
(
x => x.Select
(
(y, i) => new { y.PO, y.Sku, y.Qty, Key = i / 5 }
)
)
.GroupBy
(
x => new { x.PO, x.Key }
);
Using LINQ to create the CSV from the query results is bit of a hack, but it gets the job done. (The "benefit" of using LINQ is that you could chain the original query and the CSV generation into a single, massive statement, should you wish.)
IEnumerable<string> csvLines = query
.Select
(
x => x.Aggregate
(
new { Count = 0, SB = new StringBuilder() },
(a, y) => new
{
Count = a.Count + 1,
SB = ((a.SB.Length == 0) ? a.SB.Append(y.PO) : a.SB)
.Append(", ").Append(y.Sku).Append(", ").Append(y.Qty)
},
a => a.SB.ToString() + string.Join(", , ", new string[6 - a.Count])
)
);
string csv = string.Join(Environment.NewLine, csvLines.ToArray());
In my opinion, creating the CSV without using LINQ makes the code much more readable:
StringBuilder sb = new StringBuilder();
foreach (var group in query)
{
int count = 0;
foreach (var item in group)
{
if (count++ == 0)
{
sb.Append(item.PO);
}
sb.Append(", ").Append(item.Sku).Append(", ").Append(item.Qty);
}
while (count++ < 5)
{
sb.Append(", , ");
}
sb.Append(Environment.NewLine);
}
string csv = sb.ToString();
Here you go. I didn't format the output the way you wanted. But this should give you an idea of how to pivot rows. Hope this helps :-)
public class MyClass
{
public int PO { get; set; }
public String SKU { get; set; }
public int Qty { get; set; }
public static IEnumerable<MyClass> GetList()
{
return new List<MyClass>()
{
new MyClass {PO = 1, SKU = "ABC", Qty = 1},
new MyClass {PO = 1, SKU = "DEF", Qty = 2},
new MyClass {PO = 1, SKU = "GHI", Qty = 1},
new MyClass {PO = 1, SKU = "QWE", Qty = 1},
new MyClass {PO = 1, SKU = "ASD", Qty = 1},
new MyClass {PO = 1, SKU = "ZXC", Qty = 5},
new MyClass {PO = 1, SKU = "ERT", Qty = 1},
new MyClass {PO = 2, SKU = "QWE", Qty = 1},
new MyClass {PO = 2, SKU = "ASD", Qty = 1},
new MyClass {PO = 2, SKU = "ZXC", Qty = 5},
};
}
}
EDIT: I've fixed the query based on Luke's comment
var lQuery =
MyClass.GetList()
.GroupBy(pArg => pArg.PO)
.Select(pArg => new
{
Test = pArg.Select((pArg1, pId) =>
new {ID = (pId / 5),
pArg1.PO, pArg1.SKU, pArg1.Qty})
.GroupBy(pArg1 => pArg1.ID)
.Select(pArg1 =>
pArg1.Aggregate(pArg.Key.ToString(),
(pSeed, pCur) =>
pSeed + pCur.SKU + ","))
});