I get this error when I try to group by CellID:
Cannot implicitly convert type 'System.Collections.Generic.List
System.Linq.IGrouping int,p2pControllerLogAnalyser.Models.GSMData' to
'System.Collections.Generic.List
p2pControllerLogAnalyser.Models.GSMData'
public List<GSMData> GetCellID()
{
return Gsmdata.GroupBy(x => x.CellID).ToList();
}
What am I doing wrong and how can I fix it?
If you really need to do this, though I can't imagine why, you'll need to flatten the per-group enumerables into a single list using SelectMany:
public List<GSMData> GetCellID()
{
return Gsmdata
.GroupBy(x => x.CellID)
.SelectMany(gr => gr)
.ToList();
}
Of course, this looks like you are trying to batch items with the same CellID together, so you could always simply order it:
public List<GSMData> GetCellID()
{
return Gsmdata
.OrderBy(x => x.CellID)
.ToList();
}
Further to your comments, distinct CellID values can be returned thus:
return Gsmdata.Select(x => x.CellID).Distinct();
If you wish to return an ID and a count of grouped data, you can bundle that into an anonymous type:
return Gsmdata
.GroupBy(x => x.CellID)
.Select(gr => new { CellID = gr.Key, Count = gr.Count() });
Though if you are returning this from a method I'd make a discoverable type and not use an anonymous type.
if i understand right you need something like this
var result = (from gdata in Gsmdata
group gdata by gbata.CellID into g
select new Result{
CellID = g.Key,
Meters = g.Sum(i=>i.Meter)
}
).ToList();
where Result is
public class Result{
public /*type your CellID */ CellID;
public /*type your Meter */ Meters;
}
The problem is that GroupBy returns a grouped collection, in this case System.Collections.Generic.List<System.Linq.IGrouping<int, p2pControllerLogAnalyser.Models.GSMData>>. Your method returns a List<GSMData>.
In order to fix this, you need to adjust your method declaration and/or your Linq query so that the types match.
From your comments, I understand that you want to return a list of the distinct cell ids. You can do this by changing both the method declaration and the Linq query (I'm assuming that CellID is of type int based upon the type in the IGrouping):
public List<int> GetCellID()
{
return Gsmdata.Select(x => x.CellID)
.Distinct().ToList();
}
Related
Why my method GetList() returns null after a linq statement?
public static List<MyType> GetListOfAllLocations()
{
var DistinctList = ListWith25Elements.GroupBy(x => x.id).Select(y => y.First());
return DistinctList as List<MyType>
}
...
foreach(MyType mt in GetListOfAllLocations())... // this is null?!?!
DistinctList is an IEnumerable<MyType>, to get it as a List<MyType> you have to do
return DistinctList.ToList();
I don't believe you have to use a list in this case at all. However if you need to load the values eagerly (either because you need to enumerate them several times, or because you are in a SQL context...) you will have to do:
var distinctList = listWith25Elements.GroupBy(x => x.id).Select(y => y.First());
return distinctList.ToList()
I have written following code in which I want to calculate total sum of the price.
But it says no definition of sum. What possibly might be wrong?
public virtual IList<DesignWiseTotal> summary(int customerId)
{
var query = _orderItemRepository.Table;
return query.Where(oi => oi.Product.Designer_Id == customerId)
.Select(oi => new DesignWiseTotal
{
GrandTotal = oi.Sum(x => x.PriceExclTax),
});
}
Above code is quite similar to following code, which works fine. Following code calculates sum group by twine. What I am trying to do in above code is calculate the grand total of all twines for one customer.
public virtual IList<DesignWiseTotal> DesignWiseSplits(int customerId)
{
var query = _orderItemRepository.Table;
return query.Where(oi => oi.Product.Designer_Id == customerId)
.GroupBy(oi => oi.Product.Twine)
.Select(oi => new DesignWiseTotal
{
Total = oi.Sum(x => x.PriceExclTax),
Twine = oi.Key,
}).ToList();
}
I am attaching a screenshot of error too.
A single OrderItem (or whatever the name of the class obtained is from the repository) isn't a collection (e.g. IEnumerable, IQueryable etc) , so .Sum cannot be applied.
In the second query, you are Summing the Groups of .GroupBy(oi => oi.Product.Twine). Each group will have one or more elements, hence Sum is permissable
The choice of lambda parameter name is unfortunate .. more clear would be:
.GroupBy(oi => oi.Product.Twine)
.Select(grp => new DesignWiseTotal ...
Since it appears you want to return a simple scalar value (Grand Total), why not change the method signature to:
public virtual decimal DesignWiseSplits(int customerId)
And then
return query.Where(oi => oi.Product.Designer_Id == customerId)
.Sum(oi => oi.PriceExclTax);
(or I guess return a single DesignWiseTotal with the Sum for the filtered customer`?)
(And I'm hoping of course that oi.PriceExclTax is decimal)
Edit
If you retain this signature:
public virtual IList<DesignWiseTotal> summary(int customerId)
You'll can use:
return new List<DesignWiseTotal>
{
new DesignWiseTotal
{
GrandTotal = query.Where(oi => oi.Product.Designer_Id == customerId)
.Sum(oi => oi.PriceExclTax);
}
};
Although this does seem overkill to return a scalar as an array of a class.
Building a bunch of reports, have to do the same thing over and over with different fields
public List<ReportSummary> ListProducer()
{
return (from p in Context.stdReports
group p by new { p.txt_company, p.int_agencyId }
into g
select new ReportSummary
{
PKi = g.Key.int_agencyId,
Name = g.Key.txt_company,
Sum = g.Sum(foo => foo.lng_premium),
Count = g.Count()
}).OrderBy(q => q.Name).ToList();
}
public List<ReportSummary> ListCarrier()
{
return (from p in Context.stdReports
group p by new { p.txt_carrier, p.int_carrierId }
into g
select new ReportSummary
{
PKi = g.Key.int_carrierId,
Name = g.Key.txt_carrier,
Sum = g.Sum(foo => foo.lng_premium),
Count = g.Count()
}).OrderBy(q => q.Name).ToList();
}
My Mind is drawing a blank on how i might be able to bring these two together.
It looks like the only thing that changes are the names of the grouping parameters. Could you write a wrapper function that accepts lambdas specifying the grouping parameters? Or even a wrapper function that accepts two strings and then builds raw T-SQL, instead of using LINQ?
Or, and I don't know if this would compile, can you alias the fields in the group statement so that the grouping construct can always be referenced the same way, such as g.Key.id1 and g.Key.id2? You could then pass the grouping construct into the ReportSummary constructor and do the left-hand/right-hand assignment in one place. (You'd need to pass it as dynamic though, since its an anonymous object at the call site)
You could do something like this:
public List<ReportSummary> GetList(Func<Record, Tuple<string, int>> fieldSelector)
{
return (from p in Context.stdReports
group p by fieldSelector(p)
into g
select new ReportSummary
{
PKi = g.Key.Item2
Name = g.Key.Item1,
Sum = g.Sum(foo => foo.lng_premium),
Count = g.Count()
}).OrderBy(q => q.Name).ToList();
}
And then you could call it like this:
var summary = GetList(rec => Tuple.Create(rec.txt_company, rec.int_agencyId));
or:
var summary = GetList(rec => Tuple.Create(rec.txt_carrier, rec.int_carrierId));
Of course, you'll want to replace Record with whatever type Context.stdReports is actually returning.
I haven't checked to see if that will compile, but you get the idea.
Since all that changes between the two queries is the group key, parameterize it. Since it's a composite key (has more than one value within), you'll need to create a simple class which can hold those values (with generic names).
In this case, to parameterize it, make the key selector a parameter to your function. It would have to be an expression and the method syntax to get this to work. You could then generalize it into a function:
public class GroupKey
{
public int Id { get; set; }
public string Name { get; set; }
}
private IQueryable<ReportSummary> GetReport(
Expression<Func<stdReport, GroupKey>> groupKeySelector)
{
return Context.stdReports
.GroupBy(groupKeySelector)
.Select(g => new ReportSummary
{
PKi = g.Key.Id,
Name = g.Key.Name,
Sum = g.Sum(report => report.lng_premium),
Count = g.Count(),
})
.OrderBy(summary => summary.Name);
}
Then just make use of this function in your queries using the appropriate key selectors.
public List<ReportSummary> ListProducer()
{
return GetReport(r =>
new GroupKey
{
Id = r.int_agencyId,
Name = r.txt_company,
})
.ToList();
}
public List<ReportSummary> ListCarrier()
{
return GetReport(r =>
new GroupKey
{
Id = r.int_carrierId,
Name = r.txt_carrier,
})
.ToList();
}
I don't know what types you have mapped for your entities so I made some assumptions. Use whatever is appropriate in your case.
I have the following ItemArray:
dt.Rows[0].ItemArray.. //{0,1,2,3,4,5}
the headers are : item0,item1,item2 etc..
So far, to get a value from the ItemArray I used to call it by an index.
Is there any way to get the value within the ItemArray with a Linq expression based on the column name?
Thanks
You can also use the column-name to get the field value:
int item1 = row.Field<int>("Item1");
DataRow.Item Property(String)
DataRow.Field Method: Provides strongly-typed access
You could also use LINQ-to-DataSet:
int[] allItems = (from row in dt.AsEnumerable()
select row.Field<int>("Item1")).ToArray();
or in method syntax:
int[] allItems = dt.AsEnumerable().Select(r => r.Field<int>("Item1")).ToArray();
If you use the Item indexer rather than ItemArray, you can access items by column name, regardless of whether you use LINQ or not.
dt.Rows[0]["Column Name"]
Tim Schmelter's answer is probably what you are lookin for, just to add also this way using Convert class instead of DataRow.Field:
var q = (from row in dataTable.AsEnumerable() select Convert.ToInt16(row["COLUMN1"])).ToArray();
Here's what I've come up with today solving a similar problem. In my case:
(1)I needed to xtract the values from columns named Item1, Item2, ... of bool type.
(2) I needed to xtract the ordinal number of that ItemN that had a true value.
var itemValues = dataTable.Select().Select(
r => r.ItemArray.Where((c, i) =>
dataTable.Columns[i].ColumnName.StartsWith("Item") && c is bool)
.Select((v, i) => new { Index = i + 1, Value = v.ToString().ToBoolean() }))
.ToList();
if (itemValues.Any())
{
//int[] of indices for true values
var trueIndexArray = itemValues.First().Where(v => v.Value == true)
.Select(v => v.Index).ToArray();
}
forgot an essential part: I have a .ToBoolean() helper extension method to parse object values:
public static bool ToBoolean(this string s)
{
if (bool.TryParse(s, out bool result))
{
return result;
}
return false;
}
I have 2 lists. 1 is a collection of products. And the other is a collection of products in a shop.
I need to be able to return all shopProducts if the names match any Names in the products.
I have this but it doesn't seem to work. Any ideas?
var products = shopProducts.Where(p => p.Name.Any(listOfProducts.
Select(l => l.Name).ToList())).ToList();
I need to say give me all the shopproducts where name exists in the other list.
var products = shopProducts.Where(p => listOfProducts.Any(l => p.Name == l.Name))
.ToList();
For LINQ-to-Objects, if listOfProducts contains many items then you might get better performance if you create a HashSet<T> containing all the required names and then use that in your query. HashSet<T> has O(1) lookup performance compared to O(n) for an arbitrary IEnumerable<T>.
var names = new HashSet<string>(listOfProducts.Select(p => p.Name));
var products = shopProducts.Where(p => names.Contains(p.Name))
.ToList();
For LINQ-to-SQL, I would expect (hope?) that the provider could optimise the generated SQL automatically without needing any manual tweaking of the query.
You could use a join, for example:
var q = from sp in shopProducts
join p in listOfProducts on sp.Name equals p.Name
select sp;
A fuller guide on join is here.
You could create an IEqualityComparer<T> that says products with equal names are equal.
class ProductNameEqulity : IEqualityComparer<Product>
{
public bool Equals(Product p1, Product p2)
{
return p1.Name == p2.Name
}
public int GetHashCode(Product product)
{
return product.Name.GetHashCode();
}
}
Then you can use this in the Intersect extension method.
var products = shopProducts.Intersect(listOfProducts, new ProductNameEquality());
Try this please
var products = shopProducts.Where(m=> listOfProducts.Select(l=>l.Name).ToList().Contains(m=>m.Name));
var products = shopProducts
.Where(shopProduct =>
listOfProducts.Any(p => shopProduct.Name == p.Name))
.ToList();