I have a list which I filtered in this way:
var items = myList.Select(c => c.foo == 1)
.Aggregate((decimal s, decimal c) => s.prop + a.prop);
the compiler say:
Select does not contains a definition for Aggregate
what I did wrong??
I need to sum of the value of the list that match with 1, if I use only Where I get list does not contain a definition for aggregate which is my question.
Here is an approach that works with Aggregate.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var myList = new List<SomeObj>() { new SomeObj(1, 10), new SomeObj(1, 20) };
var sum = myList
// filter for values of foo that equal 1
.Where(c => c.foo == 1)
// accumulate those values
.Aggregate(0m, (decimal acc, SomeObj next) => acc += next.prop);
Console.WriteLine(sum);
}
}
public class SomeObj
{
public int foo { get; set; } = 1;
public int prop { get; set; } = 10;
public SomeObj(int foo, int prop)
{
this.foo = foo;
this.prop = prop;
}
}
Related
I am passing a list of objects from the Controller to a function in a Helper Class which is to return a dictionary back. However, the return value I am getting is always nothing. My list of objects works fine and the values are present so the error is in the helper class.
This is my Controller:
[HttpPost]
public ActionResult Form(List<Student> s)
{
var c = new HelperClass();
var result = c.helpermethod(s);
ViewBag.Result = s.Count; // RETURNS CORRECT LENGTH
ViewBag.Value = result.Count; // ALWAYS RETURN ZERO
return View("Index");
}
Method in my Helper Class :
public Dictionary<Person,int> helpermethod(List<Student> s)
{
var result = new Dictionary<string, int>();
List<string> k = new List<string>();
List<int> v = new List<int>();
for (int i = 0; i < s.Count; i++)
{
var d = s[i];
if (k.Contains(d.Name)){
int index = k.FindIndex(a => a == d.Name);
v[index]+= d.Age;
}
else {
k.Append(d.Name);
v.Append(d.Age);
}
}
// Create Dictionary
for (int i = 0; i < k.Count; i++)
{
var p= new Person(k[i]) ;
result.Add(Person, v[i]);
}
return result;
}
Dictionary is Person Object:
public class Person
{
public string p { get; set; }
// Intializing class
public Person(string x)
{
p = x;
}
}
Key of Dictionary is a Student Object:
This is my model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MVCModel.Models
{
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
}
}
The main issue is in this snippet:
else {
k.Append(d.Name);
v.Append(d.Age);
}
.Append() is a LINQ extension method which does not modify the original collection, it simply returns a new IEnumerable with the added element at the end. That means that k and v stay empty, and the last for-cycle in helpermethod never runs. Since k and v are List, use .Add() instead. Or even better, use ToDictionary.
Other than that, I see a few more issues in your code:
var result = new Dictionary<string, int>();
should be Dictionary<Person, int>, it shouldn't even compile as it is, since it does not match the return type of helpermethod.
result.Add(Person, v[i]);
should be result.Add(p, v[i]);, Person is a type.
First off, where is the Person variable declared in your code?
...
// Create Dictionary
for (int i = 0; i < k.Count; i++)
{
var p= new Person(k[i]) ;
result.Add(Person, v[i]); // <-- What is Person here?? Did you mean p?
}
Secondly, why not just use:
var res = s.ToDictionary(x => x.Name, x => x.Age);
No need to reinvent the wheel.
Furthermore, why not expand the Person class to hold the age and return a List<Person> instead of a Dictionary<string, int>?
Like this:
public class Person
{
public string Name { get; set; }
public string Age { get; set; }
// Intializing class
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
Then you can use it like this:
...
var persons = s.Select(student => new Person(student.Name, student.Age);
ViewBag.Value = persons.Count();
it's much easier to understand and is less error-prone.
So I'm not sure if this is possible, but I'm doing a final select query, and I've noticed that it would be easier for my data if I could do something like:
var finalQuery = selectQuery
.GroupBy(x => x.ProductName)
.Select(c => new
{
c.Key = c.Count()
}).ToList();
Therefore, I want the returned data to be something like:
[{
"Clothes" : 5,
"Shoes" : 7,
"Laptop" : 10
}]
My current query is:
var finalQuery = selectQuery
.GroupBy(x => x.ProductName)
.Select(c => new
{
ProductName = c.Key
ProductCount = c.Count()
}).ToList();
I thought something like c.Key.ToString() would do the trick, but it doesn't work. I'm guessing the "Key" must be a set value to work, and it can't necessarily be dynamically changed?
You can try this way
var finalQuery = selectQuery.GroupBy(p => p.ProductName).ToDictionary(g => g.Key, g => g.Count());
You should customize the string using your own code, or you can use an external library as NewtonSoft.Json.
Below is a sample code that does the trick for you.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Test
{
public static void Main()
{
var products = new List<Product>()
{
new Product("a", "Clothes"),
new Product("b", "Shoes"),
new Product("c", "Clothes"),
new Product("d", "Clothes"),
new Product("e", "Shoes"),
new Product("f", "Shoes"),
new Product("g", "Laptop"),
new Product("h", "Laptop"),
new Product("h", "Shoes"),
};
var result = products
.GroupBy(p => p.Type)
.Select(
group => new
ProductCount(group.Key, group.Count())
// You can use also an object as the commented code below
//{
// Type = group.Key,
// Count = group.Count()
//}
)
.ToList();
Console.WriteLine(ProductCount.ToFormatedString(result));
}
class Product
{
public string Name { get; set; }
public string Type { get; set; }
public Product(string name, string type)
{
this.Name = name;
this.Type = type;
}
}
class ProductCount
{
public string Type { get; set; }
public int Count { get; set; }
public ProductCount(string type, int count)
{
this.Type = type;
this.Count = count;
}
public override string ToString()
{
return $"\"{this.Type}\" : {this.Count}";
}
public static string ToFormatedString(IEnumerable<ProductCount> products) // if you need a more generic method, u can make this an extension method and use Reflection
// Or u can use a nuget package that formats objects to json (e.g: Newtonsoft is a good library)
{
var sb = new StringBuilder();
sb.AppendLine("[{");
foreach (var item in products)
sb.AppendLine(item.ToString());
sb.AppendLine("}]");
return sb.ToString();
}
}
}
I have two lists of data (they are using the same class "SaleNumber").
Each list contains a list of sale numbers. The first list is taken from the danish "DK" database and the other from the swedish database.
Right now I am looping through the danish list For each item I loop through I find the item with the same variant id in the swedish list and then I join the data into a new list called saleNumbers.
The problem with this is that because I loop through the danish list then if the danish list doesn't have salenumbers for that variant id then it won't loop through this variant. If this happens then the swedish list item won't be added either - and therefore the salenumbers item won't be created - even though it should - it should have a 0 in salenumbers.totalsalesDK and the actual salenumber for the salenumbers.totalsalesSE.
How do I merge the two together into salenumbers without missing any variants?
I still want the structure retained - so that for instance I have the SaleNumbers.TotalSales showing sum of totalsales for both dk and se together. And the SaleNumbers.TotalSalesDK showing DK sales and SaleNumbers.TotalSalesSE showing SE sales for that item. The primary unique key is always the variantId. Here is my current code:
private List<SaleNumber> ConvertDataTableToSaleNumbers(DataTable dt)
{
List<SaleNumber> saleNumbers = new List<SaleNumber>();
foreach (DataRow dr in dt.Rows)
{
saleNumbers.Add(new SaleNumber() { ProductId = int.Parse(dr["productid"].ToString()), TotalSales = int.Parse(dr["totalsales"].ToString()), VariantId = int.Parse(dr["variantid"].ToString()) });
}
return saleNumbers;
}
DataTable dtDK = new Shoply.Data.DLOrderDetail().GetNumberOfSalesSinceOrderId(constDaysAgo,
Shoply.Data.DLBasis.GetTheConnectionToTheLanguage("dk"));
DataTable dtSE = new Shoply.Data.DLOrderDetail().GetNumberOfSalesSinceOrderId(constDaysAgo,
Shoply.Data.DLBasis.GetTheConnectionToTheLanguage("se"));
List<SaleNumber> saleNumbersDK = ConvertDataTableToSaleNumbers(dtDK);
List<SaleNumber> saleNumbersSE = ConvertDataTableToSaleNumbers(dtSE);
var saleNumbers = saleNumbersDK.SelectMany
(
foo => saleNumbersSE.Where(bar => foo.VariantId == bar.VariantId).DefaultIfEmpty(),
(foo, bar) => new SaleNumber
{
VariantId = foo.VariantId,
ProductId = foo.ProductId,
TotalSales = foo.TotalSales + (bar == null ? 0 : bar.TotalSales),
TotalSalesDK = foo.TotalSales,
TotalSalesSE = (bar == null ? 0 : bar.TotalSales)
}
);
EDIT:
Code updated to perform outerjoin
How about using Join in Linq.
Simple dotnetfiddle can be seen here : Dotnetfiddle link
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main(string[] args)
{
List<SaleNumber> saleNumbersDK = new List<SaleNumber> {
new SaleNumber() { VariantId="a",ProductId="A",TotalSales=10 },
new SaleNumber() { VariantId="b",ProductId="B",TotalSales=20 }
};
List<SaleNumber> saleNumbersSE = new List<SaleNumber> {
new SaleNumber() { VariantId="a",ProductId="A",TotalSales=10 },
new SaleNumber() { VariantId="c",ProductId="c",TotalSales=30 }
};
var innerjoin = saleNumbersDK.Join(saleNumbersSE, d => d.VariantId, s => s.VariantId, (d, s) =>
{
return new SaleNumber()
{
VariantId = d.VariantId,
ProductId = d.ProductId,
TotalSales = d.TotalSales+ (s == null ? 0 : s.TotalSales),
TotalSalesDK = d.TotalSales,
TotalSalesSE = (d == null ? 0 : d.TotalSales)
};
});
var pendingright= saleNumbersSE.Except(innerjoin, new CustomComparer());
var pendingleft = saleNumbersDK.Except(innerjoin, new CustomComparer());
var salesNumber= innerjoin.Concat(pendingright).Concat(pendingleft);
foreach (var sale in salesNumber)
{
Console.WriteLine(sale);
}
//Console.ReadLine();
}
}
public class SaleNumber
{
public string VariantId { get; set; }
public string ProductId { get; set; }
public int TotalSales { get; set; }
public int TotalSalesDK { get; set; }
public int TotalSalesSE { get; set; }
public override string ToString()
{
return VariantId+"-"+ProductId+"-"+TotalSales+"-"+TotalSalesDK+"-"+TotalSalesSE;
}
}
public class CustomComparer : IEqualityComparer<SaleNumber>
{
public bool Equals(SaleNumber x, SaleNumber y)
{
return x.VariantId == y.VariantId;
}
public int GetHashCode(SaleNumber obj)
{
return obj.VariantId.GetHashCode();
}
}
Assuming ProductId is the same for DK and SE you can use a group by function like this to get the result you want.
testDK.ForEach(s => s.TotalSalesDK = s.TotalSales);
testSE.ForEach(s => s.TotalSalesSE = s.TotalSales);
testDK.Concat(testSE)
.GroupBy(s => s.VariantId)
.Select(g => new SaleNumber() {
VariantId = g.First().VariantId,
ProductId=g.First().ProductId,
TotalSales = g.Sum(s=>s.TotalSalesDK) + g.Sum(s=>s.TotalSalesSE),
TotalSalesDK=g.Sum(s=>s.TotalSalesDK),
TotalSalesSE=g.Sum(s=>s.TotalSalesSE)
}).ToList()
You can use Concat and ToList methods:
var allProducts = productCollection1.Concat(productCollection2)
.Concat(productCollection3)
.ToList();
I have a collection of objects to be ordered by an object's field value. Current problem is that the order depends on a business logic.
public enum Order : byte {
a = 1,
b = 2,
c = 3
}
public class Foo{
public long A {get;set;}
public long B {get;set;}
public long C {get;set;}
}
public class Worker(){
private Foo[] orderFoos(Foo[] foos, Func<Order, long> sort){
return foos.OrderByDescending(f => sort(f)).ToArray(foos.Length);
}
public void Work(){
Foo[] foos = getFoos();
var orderByA = orderFoos(foos, f => f.A);
var orderByB = orderFoos(foos, f => f.B);
var orderByC = orderFoos(foos, f => f.C);
}
}
Compiler throws an error that Argument 1: cannot convert from 'Foo' to 'Order'. Are there any workarounds or solutions?
It seems what you wanted to achieve is sorting on different fields. You may not need to have Order enum if it's only for that purpose and replace:
private Foo[] orderFoos(Foo[] foos, Func<Order, long> sort){
return foos.OrderByDescending(f => sort(f)).ToArray(foos.Length);
}
into
private Foo[] orderFoos(Foo[] foos, Func<Foo, long> sort){
return foos.OrderByDescending(sort).ToArray(foos.Length);
}
NB: I'm not sure your intention with adding foos.Length in the ToArray method, but supposedly that's out of the scope of the question.
The below code seems to work. It is a small change to the orderFoos method, with some sample code for you to test the results.
using System;
using System.Linq;
public enum Order : byte
{
a = 1,
b = 2,
c = 3
}
public class Foo
{
public long A { get; set; }
public long B { get; set; }
public long C { get; set; }
}
public class Worker
{
private Foo[] orderFoos(Foo[] foos, Func<Foo, long> sort)
{
return foos.OrderByDescending(sort).ToArray();
}
public void Work()
{
Foo[] foos = { new Foo() { A = 1, B = 2, C = 3 }, new Foo() { A = 10, B = 1, C = 2 }, new Foo() { A = -1, B = 1, C = 10 } };
var orderByA = orderFoos(foos, f => f.A);
var orderByB = orderFoos(foos, f => f.B);
var orderByC = orderFoos(foos, f => f.C);
Console.WriteLine(orderByA.First().A); // I expect the second to be first here so 10
Console.WriteLine(orderByB.First().A); // I expect the first to be first here so 1
Console.WriteLine(orderByC.First().A); // I expect the third to be first here so -1
}
}
class Program
{
static void Main(string[] args)
{
var worker = new Worker();
worker.Work();
Console.ReadLine();
}
}
#hsoesanto gave a good solution but it doesn't work the way I expected it would be.
So I've created temporary workaround.
private Func<Foo, long> GetOrderFunction(Order orderType)
{
switch (orderType)
{
case Order.A:
return (f) => f.A;
case Order.B:
return (f) => f.B;
case Order.C:
return (f) => f.C;
}
}
private Foo[] orderFoos(Foo[] foos, Order order)
{
var orderFunction = GetOrderFunction(order);
return foos
.OrderByDescending(f => orderFunction (f))
.ToArray(foos.Length);
}
I have class Value:
class Value
{
public Guid Guid {get;set;}
public double Val {get;set;}
}
So, if i have two big lists of Value, how can i sort it and then use BinarySearch?
Can i do it?
Thank you!
Summary:
If you have a list of Value and you want to sort your list according the Guid you have two possiblities. Either you sort the list explicitly by the property you want or you implement IComparable<Value>
var orderedList = list.OrderBy(i => i.Guid);
the other case is:
public class Value: IComparable<Value>
{
public int CompareTo(Value other)
{
if(other == null)
{
return 1;
}
return Guid.CompareTo(other.Guid);
}
//[...]
}
then you can sort:
var orderedList = list.OrderBy(i => i).ToList();
Then when you want to perform binary searching just apply following line:
int index = orderedList.BinarySearch(new Value{ Guid = guidToSearchFor });
Example:
using System.IO;
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<Value> list = new List<Value>
{
new Value() { Guid = Guid.NewGuid() },
new Value() { Guid = Guid.NewGuid() },
new Value() { Guid = Guid.NewGuid() },
new Value() { Guid = Guid.NewGuid() }
};
var orderedList = list.OrderBy(i => i).ToList();
int index = orderedList.BinarySearch(new Value{ Guid = list[2].Guid });
Console.WriteLine(index);
}
}
public class Value: IComparable<Value>
{
public int CompareTo(Value other)
{
if(other == null)
{
return 1;
}
return Guid.CompareTo(other.Guid);
}
public Guid Guid {get;set;}
public double Val {get;set;}
}
Let List<Value> valList = new List<Value>(); be your List of Value Then you can sort them based on property Val like the following:
valList = valList.OrderBy(x => x.Val).ToList();