query linq about 2 lists group by - c#

I have the following query in linq, which takes 2 lists as a data source. The first contains a list of ProductID and its description
public class Venta
{
public string ProductoId { get; set; }
public string clienteRut { get; set; }
}
public class Ventas
{
public List<Venta> lstVentas { get; set; }
}
and the other list has the products sold
public class Productos
{
public List<Producto> lstProductos { get; set; }
}
public class Producto
{
public string id { get; set; }
public string name { get; set; }
}
I need to consult the 5 most sold products, ordered by quantity from the most sold, to the least sold.
So far I have the following linq query, but I do not know how to do it so that I am given the list of the first 5, ordered from highest to lowest based on the quantity (cont)
Venta vta1 = new Venta();
vta1.ProductoId = "1";
vta1.clienteRut = "121370654";
Venta vta2 = new Venta();
vta2.ProductoId = "2";
vta2.clienteRut = "121370654";
Venta vta3 = new Venta();
vta3.ProductoId = "3";
vta3.clienteRut = "121370654";
List<Venta> lstVentasDia = new List<Venta>();
lstVentasDia.Add(vta1);
lstVentasDia.Add(vta2);
lstVentasDia.Add(vta3);
VentasDia vtas = new VentasDia();
vtas.date = "2018-05-01";
vtas.lstVentas = lstVentasDia;
var Lista5Top = from vendidos in vtas.lstVentas
orderby vendidos.ProductoId
group vendidos by vendidos.ProductoId into Grupo
select new { key = Grupo.Key, cont = Grupo.Count() };
I need in addition to that group of result, add the name of the product that is in the list Products, and order it by quantity sold of greater to less only the first 5
Thankful in advance
Gloria

Try following :
Productos productos = new Productos();
var Lista5Top = (from vendidos in vtas.lstVentas
join prod in productos.lstProductos on vendidos.ProductoId equals prod.id
select new { id = vendidos.ProductoId, rut = vendidos.clienteRut, name = prod.name })
.OrderBy(x => x.id)
.GroupBy(x => x.id)
.Select(x => new { id = x.Key, cont = x.Count(), name = x.FirstOrDefault().name })
.OrderByDescending(x => x.cont)
.Take(5).ToList();

Related

Linq counting second grouping and counting without grouping

I'm trying to build a summary query that i will be using for statistics.
i have a dataTable with the folowing columns (approx 18000 rows) :
Artist / Album / file_path (one for each song) / rating /
each artist has 1 or several album with has songs and each songs have a rating
I want to have the following result :
For each artist ID (more reliable than the artist name), the total number of albums, the total number of songs, and the total number of ratings equal to 5.
Artist x / #album / #songs / #rating = 5 / song.first() //in song.first i have access to the file path, it can be any file path from the artist hence the first one.
I've been pulling my hair for several hours now and i cannot manage to get the # of albums per artist :( This is what i've been trying so far :
i have a Class for the query :
public class art_detail
{
public string artiste { get; set; }
public string fp { get; set; } // the file_path
public int nbr_album { get; set; }
public int nbr_song { get; set; }
public int nbr_rat5 { get; set; }
}
this is the query i came up to :
var result = from res in Globals.ds.Tables[0].AsEnumerable() // the table
.GroupBy(x => new { art = x.Field<int>("Artist_ID"), alb = x.Field<string>("album") })
.Select(x => new art_detail { artiste = x.Select(p =>p.Field<string>("artiste")).First(), fp = x.Select(p=>p.Field<string>("file_path")).First(), nbr_album = x.Key.alb.Count() })
.OrderBy(x => x.artiste)
select res;
The count is unfortunately completely wrong and i have no idea how to get the # of rating = 5 :(
Thanks for the help !
Edit :
Here is my query to make it work :
var table = Globals.ds.Tables[0].AsEnumerable();
var stats = table.GroupBy(x => x.Field<int>("Artist_ID"))
.Select(x => new art_detail
{
artiste = x.Select(p=>p.Field<string>("artiste")).First(),
nbr_album = x.Select(y => y.Field<string>("album")).Distinct().Count(),
fp = x.Select(y => y.Field<string>("file_path")).FirstOrDefault(),
nbr_song = x.Count(),
nbr_rat5 = x.Count(y => y.Field<int>("Rating") == 5)
});
Simpler than what i thought :)
Assuming a table whose schema matches this class:
public class Song
{
public string ArtistID { get; set; }
public string Album { get; set; }
public string FilePath { get; set; }
public int Rating { get; set; }
}
and given a LINQ source, you have the following query:
IQueryable<Song> table = /*insert source*/;
var stats = table.GroupBy(x => x.ArtistID);
.Select(x => new art_detail
{
artiste = x.Key,
nbr_album = x.Select(y => y.Album).Distinct().Count(),
nbr_song = x.Count(),
nbr_rat5 = x.Count(y => y.Rating == 5),
});
I used head compiled query as it seemed more understandable for me in this case:
Example model:
public class Artist
{
public string ArtistID { get; set; }
public string Album { get; set; }
public string FilePath { get; set; }
public int Rating { get; set; }
public int NumberOfSongs { get; set; }
}
Creating some dummy records for Usher and Beyonce:
//Usher
var artistOne = new Artist()
{
ArtistID = "Usher",
Album = "Lit",
FilePath = "dummy/path/here",
Rating = 5,
NumberOfSongs = 9
};
var artistTwo = new Artist()
{
ArtistID = "Usher",
Album = "Sick",
FilePath = "dummy/path/here",
Rating = 5,
NumberOfSongs = 11
};
var artistThree = new Artist()
{
ArtistID = "Usher",
Album = "Dope",
FilePath = "dummy/path/here",
Rating = 4,
NumberOfSongs = 14
};
//Beyonce
var artistFour = new Artist()
{
ArtistID = "Beyonce",
Album = "Hot",
FilePath = "dummy/path/here",
Rating = 5,
NumberOfSongs = 8
};
var artistFive = new Artist()
{
ArtistID = "Beyonce",
Album = "Fire",
FilePath = "dummy/path/here",
Rating = 4,
NumberOfSongs = 16
};
var listOfArtist = new List<Artist> { artistOne, artistTwo, artistThree, artistFour, artistFive };
Running query:
var result = from a in listOfArtist
where a.Rating == 5
group a by a.ArtistID into art
select new
{
artist = art.Key,
numberOfAlbums = art.Count(),
numberOfSongs = art.Sum(d => d.NumberOfSongs),
};
Results:
Hope this helps =)

Linq, how to group data by ID and merge data within that ID

From the database I get the following values
PlanningID = GetValue<int>(dataReader["PlanningID"]),
PlanningStatus = GetValue<string>(dataReader["PlanningStatus"]),
Private = GetValue<int>(dataReader["Private"])
Social = GetValue<int>(dataReader["Social"])
PlanningID, PlanningStatus, Private
1, good, 10
1, fair, 5
1, bad, 1
I want to group these by planningID so that it looks like this
public class ClassResult
{
public int planningID { get; set; }
public List<PlanningStatus> PlanningStatus { get; set; }
}
public class PlanningStatus
{
public string PlanningStatus { get; set; }
public int Private { get; set; }
public int Social{ get; set; }
}
I tried this but the output was wrong:
IEnumerable<ClassResult> classResult =
from result in results
group result by new
{
result.PlanningID,
result.PlanningStatus
} into grouping
select new ClassResult
{
PlanningID = grouping.Key.PlanningID,
PlanningStatus = grouping.Key.PlanningStatus,
Private = grouping.First().Private,
Social = grouping.First().Social
};
return lrrResults;
To be honest I got no idea how to do this
Updated projection as advised below but still have multiple planning id's of lets say 1
var results =
from result in results
group result by result.PlanningID
into grouping
select new ClassResult
{
PlanningID = grouping.Key,
PlanningStatusType = grouping.Select(item => new PlanningStatusType
{
PlanningStatus = item.PlanningStatus,
Private = item.Private,
Social = item.Social,
}).ToList(),
LatestChange = grouping.First().LatestChange,
WeeklyChangeType = grouping.First().WeeklyChangeType,
Address = grouping.First().Address
};
In your query above you are grouping by both the PlanningID and PlanningStatus so the groups will contain 1 item each. What you want to do is as following:
Instantiate a new ClassResult as you did but setting the planningID but the .Key (which is now just a single property
Project each item of the grouping to a new object of type PlanningStatus using the .Select()
Code:
var classResult = from result in results
group result by result.PlanningID into grouping
select new ClassResult
{
PlanningID = grouping.Key,
PlanningStatus = grouping.Select(item => new PlanningStatus {
PlanningStatus = item.PlanningStatus,
Private = item.Private,
Social = item.Social
}).ToList()
};
Tested on some sample code and works:

Using Linq to read from multiple tables

I'm sure someone else has asked this but I searched on what I could think of to find the solution.
I've got the following data models to match tables in my SQL db:
public class ProfileDetailModel
{
public string id { get; set; }
public string name { get; set; }
public StyleList[] styleList { get; set; }
public FabricList[] fabricList { get; set; }
}
public class StyleList
{
public string id { get; set; }
public string name { get; set; }
}
public class FabricList
{
public string id { get; set; }
public string fabricName { get; set; }
}
This is the current query code:
var query = (from t in db.tblProfiles
select new ProfileDetailModel()
{
id = t.id,
name = t.name
});
var querylist = await query.ToListAsync();
(prototyped linq queries below for style and fabric)
var styleQuery = (from t in db.tblStyles
select new styleList()
{
id = t.id,
name = t.name
});
var fabricQuery = (from t in db.tblFabrics
select new fabricList()
{
id = t.id,
name = t.name
});
if (queryList.Count > 0)
{
var item = queryList[0];
item.styleList = styleQuery;
item.fabricList = fabricQuery;
}
I'll have one profileDetailModel with multiple items in styleList and in fabricList. EG.
ProfileDetailModel
Data: Pants
styleList: Bell Bottom, Straight Leg, Boot fit
fabricList: jean-blue, jean-black, plaid
All three above models are tables in my db. I could issue 3 separate queries to read the data then assemble after the fact. But is there a way I can do a linq query to include the two arrays in the main query in one shot?
Try this:
var newQuery = (from p in db.tblProfiles
select p)
.AsEnumerable()
.Select(x => new ProfileDetailModel()
{
id = x.id,
name = x.name,
styleList = styleQuery,
fabricList = fabricQuery
});

LINQ Create a nested List

I am trying to convert a flat list to nested list.
This is my flat list:
public class DefectLocationCount {
public string LocationName { get; set; }
public string DefectName { get; set; }
public int TotalCount { get; set; }
}
This is my nested list:
public class DefectLocationOutput
{
public string DefectName{ get; set; }
public List<int> TotalCount{ get; set; }
}
This is my linq query to populate the flat list
var results = (from def in rep.tblDefects
join defLoc in
(from defDet in rep.tblMovementDefects
join det in rep.tblMovementDetails on defDet.MovementDetailId equals det.MovementDetailId
join loc in rep.tblLocations on det.ToLocationId equals loc.LocationId
join hed in rep.tblMovementHeaders on det.MovementHeaderId equals hed.MovemenHeaderId
where hed.DateCreated >= fromDate && hed.DateCreated <= toDate
select new { loc.LocationName, defDet.DefectId, loc.LocationId }
) on def.DefectId equals defLoc.DefectId
group def by new { def.DefectName, defLoc.LocationId, defLoc.LocationName } into joined
select new
{
LocationName = joined.Key.LocationName,
LocationId = joined.Key.LocationId,
DefectName = joined.Key.DefectName,
TotalCount = joined.Count()
}).OrderBy(x => x.LocationId);
Edit
This is what i am trying to achieve
In the format of List<DefectLocationOutput> and instead of null values have 0.
How can this be done?
I didn't really understand what result are you awaiting, but maybe this will help you:
DefectLocationCount cl1 = new DefectLocationCount()
{
DefectName = "Name1",
LocationName = "Location1",
TotalCount = 2
};
DefectLocationCount cl2 = new DefectLocationCount()
{
DefectName = "Name1",
LocationName = "Location2",
TotalCount = 3
};
DefectLocationCount cl3 = new DefectLocationCount()
{
DefectName = "Name2",
LocationName = "Location3",
TotalCount = 6
};
var lstCl = new List<DefectLocationCount>();
lstCl.Add(cl1);
lstCl.Add(cl2);
lstCl.Add(cl3);
var result = lstCl.GroupBy(x => x.DefectName).Select(x => new DefectLocationOutput() { DefectName = x.Key, TotalCount = lstCl.Where(y => y.DefectName == x.Key).Select(y => y.TotalCount).ToList() });

Filtering a List using lambda

I have an object which has properties ID, brandID, brandName, NumPages, and Type.
i need to show the top 5 brands by numPage size, a brand may have multiple IDs, so I need to group by brand
listing.OrderByDescending(o => o.numPage).GroupBy(o=> o.brandName).Take(5).ToList();
is alone the lines of what im looking for but this is not valid code.
It sounds like a given brand name may have several ID's and that you want the top 5 brand's sorted by numPage. Is that correct
If so try the following
var query = listing
.GroupBy(x => x.brandName)
.OrderByDescending(brands => brands.Sum(x => x.numPage))
.Select(x => x.Key)
.Take(5);
Note: After the GroupBy operation you're now passing around a collection of the brand objects instead of single ones. Hence to order by the numPage we need to sum it for all of the brand objects in the group. The .Select(x => x.Key) will select back out the original brandName on which the group is based
just tried and it works:
public class Listing
{
public int ID { get; set; }
public int BrandID { get; set; }
public string BrandName { get; set; }
public int NumPages { get; set; }
public Type Type { get; set; }
}
Here the filtering
Listing listing1 = new Listing() { NumPages = 2, BrandName = "xx" };
Listing listing2 = new Listing() { NumPages = 2, BrandName = "xx" };
Listing listing3 = new Listing() { NumPages = 2, BrandName = "xx" };
Listing listing4 = new Listing() { NumPages = 3, BrandName = "xxxxx" };
List<Listing> allListings = new List<Listing>() { listing1, listing2, listing3, listing4 };
var result = allListings.OrderByDescending(x => x.NumPages).GroupBy(x => x.BrandName).Take(5);

Categories

Resources