Set the logic of value usage - algorithm

I want to implement a logic "If this_field is initialized, then: get its value, else: use the average value amongst all the initialized this_field in all listed objects instead". I know I can check the None in Foreach cycle, using specific field, but I want one function/interface/smth to use on all the fields (there are 50+ of them, so overloading might be too long, and too strict, in case I'll add more fields to my class).
For examle:
Foreach (City thiscity in Cities) {
thiscity.PolutionRoughEstimation = thiscity.PolutionData.MetalsPortion != None ?
thiscity.PolutionData.MetalsInAirPortion :
AverageKnown(Cities, Object.PolutionData.MetalsPortion) ;
}
Yet, I do not whant to write the overloading of AverageKnown for all parameters. I also don't want to write the same Foreach loop for each field. I just whant my system to use the average value of this group, whenever I have no data for it. There might be a way to write it once, but I just cannot figure it out.
Help me, please.

In C#, you can do this, by simply use Average method. See this:
static void Main(string[] args)
{
List<City> cities = new List<City>();
cities.Add(new City { Polution = 1, OtherField = 1 });
cities.Add(new City { Polution = 5, OtherField = 5 });
cities.Add(new City { OtherField = 2 });
double? ave = cities.Average(c => c.Polution);
}
public class City
{
public int? Polution;
public int? OtherField;
}
The value of ave is 3 not 2.
and so, it depends on the implementation of your programming language/lib.

Now I figured it out how to use reflections with expressions to fill the missing data
using System.Reflection;
public void FillGapInData(ref List<City> cities)
{
foreach (var city in cities)
{
Type t = city.GetType();
PropertyInfo[] parameteres = t.GetProperties();
foreach (var parameter in parameteres)
{
if (((double?)parameter.GetValue(city)).Equals(null))
{
double? a = cities
.Average(c => (double?)parameter.GetValue(c));
parameter.SetValue(city, a);
}
}
}
}
More accurate version, to ensure we do not spoil our string properties, and with -1 as an explicit mark for "missing data":
using System.Reflection;
public void FillGapInData(ref List<City> cities)
{
foreach (var city in cities)
{
Type t = city.GetType();
PropertyInfo[] parameteres = t.GetProperties();
foreach (var parameter in parameteres)
{
if (parameter.PropertyType == typeof(double?)) //check we have a number here
if ((((double?)parameter.GetValue(city)) == -1) || //check for -1 reserved mark
(((double?)parameter.GetValue(city)).Equals(null)))
{
double? a = cities
.Where(c => ( ((double?)(parameter.GetValue(c)) != -1)
&& !(((double?)(parameter.GetValue(c))).Equals(null)))) //I couldn't stop
.Average(c => (double?)(parameter.GetValue(c)));
parameter.SetValue(city, a);
}
}
}
}

Related

How to select multiple class properties in LINQ Expression?

If I have a class like this
`
class Person
{
public string First;
public string Last;
public bool IsMarried;
public int Age;
}`
Then how can I write a LINQ Expression where I could select properties of a Person. I want to do something like this (user can enter 1..n properties)
SelectData<Person>(x=>x.First, x.Last,x.Age);
What would be the input expression of my SelectData function ?
SelectData(Expression<Func<TEntity, List<string>>> selector); ?
EDIT
In my SelectData function I want to extract property names and then generate SELECT clause of my SQL Query dynamically.
SOLUTION
Ok, so what I have done is to have my SelectData as
public IEnumerable<TEntity> SelectData(Expression<Func<TEntity, object>> expression)
{
NewExpression body = (NewExpression)expression.Body;
List<string> columns = new List<string>();
foreach(var arg in body.Arguments)
{
var exp = (MemberExpression)arg;
columns.Add(exp.Member.Name);
}
//build query
And to use it I call it like this
ccc<Person>().SelectData(x => new { x.First, x.Last, x.Age });
Hopefully it would help someone who is looking :)
Thanks,
IY
I think it would be better to use delegates instead of Reflection. Apart from the fact that delegates will be faster, the compiler will complain if you try to fetch property values that do not exist. With reflection you won't find errors until run time.
Luckily there is already something like that. it is implemented as an extension function of IEnumerable, and it is called Select (irony intended)
I think you want something like this:
I have a sequence of Persons, and I want you to create a Linq
statement that returns per Person a new object that contains the
properties First and Last.
Or:
I have a sequence of Persns and I want you to create a Linq statement
that returns per Person a new object that contains Age, IsMarried,
whether it is an adult and to make it difficult: one Property called
Name which is a combination of First and Last
The function SelectData would be something like this:
IEnumerable<TResult> SelectData<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
return source.Select(selector);
}
Usage:
problem 1: return per Person a new object that contains the
properties First and Last.
var result = Persons.SelectData(person => new
{
First = person.First,
Last = person.Last,
});
problem 2: return per Person a new object that contains Age, IsMarried, whether he is an adult and one Property called Name which is a combination
of First and Last
var result = Persons.SelectData(person => new
{
Age = person.Name,
IsMarried = person.IsMarried,
IsAdult = person.Age > 21,
Name = new
{
First = person.First,
Last = person.Last,
},
});
Well let's face it, your SelectData is nothing more than Enumerable.Select
You could of course create a function where you'd let the caller provide a list of properties he wants, but (1) that would limit his possibilities to design the end result and (2) it would be way more typing for him to call the function.
Instead of:
.Select(p => new
{
P1 = p.Property1,
P2 = p.Property2,
}
he would have to type something like
.SelectData(new List<Func<TSource, TResult>()
{
p => p.Property1, // first element of the property list
p -> p.Property2, // second element of the property list
}
You won't be able to name the returned properties, you won't be able to combine several properties into one:
.Select(p => p.First + p.Last)
And what would you gain by it?
Highly discouraged requirement!
You could achive similar result using Reflection and Extension Method
Model:
namespace ConsoleApplication2
{
class Person
{
public string First { get; set; }
public string Last { get; set; }
public bool IsMarried { get; set; }
public int Age { get; set; }
}
}
Service:
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public static class Service
{
public static IQueryable<IQueryable<KeyValuePair<string, object>>> SelectData<T>(this IQueryable<T> queryable, string[] properties)
{
var queryResult = new List<IQueryable<KeyValuePair<string, object>>>();
foreach (T entity in queryable)
{
var entityProperties = new List<KeyValuePair<string, object>>();
foreach (string property in properties)
{
var value = typeof(T).GetProperty(property).GetValue(entity);
var entityProperty = new KeyValuePair<string, object>(property, value);
entityProperties.Add(entityProperty);
}
queryResult.Add(entityProperties.AsQueryable());
}
return queryResult.AsQueryable();
}
}
}
Usage:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
class Program
{
static void Main(string[] args)
{
var list = new List<Person>()
{
new Person()
{
Age = 18,
First = "test1",
IsMarried = false,
Last = "test2"
},
new Person()
{
Age = 40,
First = "test3",
IsMarried = true,
Last = "test4"
}
};
var queryableList = list.AsQueryable();
string[] properties = { "Age", "Last" };
var result = queryableList.SelectData(properties);
foreach (var element in result)
{
foreach (var property in element)
{
Console.WriteLine($"{property.Key}: {property.Value}");
}
}
Console.ReadKey();
}
}
}
Result:
Age: 18
Last: test2
Age: 40
Last: test4

List Distinct is not working in LINQ instead of GroupBy

Thanks in advance. I can get required output when using var but i want to get required output by using Distinct in List<>.
InventoryDetails.cs
public class InventoryDetails
{
public int? PersonalInventoryGroupId { get; set; }
public int? PersonalInventoryBinId { get; set; }
}
InventoryController.cs
[HttpGet("GetInventory")]
public IActionResult GetInventory(int id)
{
//Below code will return distinct record
var inventory = (from i in _context.TempTbl
where i.TempId == id
select new
{
PersonalInventoryBinId = i.PersonalInventoryBinId,
PersonalInventoryGroupId = i.PersonalInventoryGroupId,
}).ToList().Distinct().ToList();
//Below code is not doing distinct
List<InventoryDetails> inventory = (from i in _context.TempTbl
where i.TempId == id
select new InventoryDetails
{
PersonalInventoryBinId = i.PersonalInventoryBinId,
PersonalInventoryGroupId = i.PersonalInventoryGroupId,
}).ToList().Distinct().ToList();
}
If i use var as return type, then i am able to get distinct records. Could some one assist it.
Please try like this it may help.
IList<InventoryDetails> inventory = _context.InventoryDetails.Where(x=>x.TempId == id).GroupBy(p => new {p.PersonalInventoryGroupId, p.PersonalInventoryBinId } )
.Select(g => g.First())
.ToList();
You need to override Equals and GetHashCode.
First, let's see the AnonymousType vs InventoryDetails
var AnonymousTypeObj1 = new { PersonalInventoryGroupId = 1, PersonalInventoryBinId = 1 };
var AnonymousTypeObj2 = new { PersonalInventoryGroupId = 1, PersonalInventoryBinId = 1 };
Console.WriteLine(AnonymousTypeObj1.Equals(AnonymousTypeObj2)); // True
var InventoryDetailsObj1 = new InventoryDetails { PersonalInventoryBinId = 1, PersonalInventoryGroupId = 1 };
var InvertoryDetailsObj2 = new InventoryDetails { PersonalInventoryBinId = 1, PersonalInventoryGroupId = 1 };
Console.WriteLine(InventoryDetailsObj1.Equals(InvertoryDetailsObj2)); // False
You can see the Equals behave differently which make Distinct behave differently. The problem is not var you mentioned in your question but AnonoymizeType
To make Distinct works as you expect, you need to override Equals and GetHashCode
public class InventoryDetails
{
public int? PersonalInventoryGroupId { get; set; }
public int? PersonalInventoryBinId { get; set; }
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj is InventoryDetails)
{
if (PersonalInventoryGroupId == (obj as InventoryDetails).PersonalInventoryGroupId
&& PersonalInventoryBinId == (obj as InventoryDetails).PersonalInventoryBinId)
return true;
}
return false;
}
public override int GetHashCode()
{
int hash = 17;
hash = hash * 23 + PersonalInventoryBinId.GetHashCode();
hash = hash * 23 + PersonalInventoryGroupId.GetHashCode();
return hash;
}
}
Another approach would be
List<InventoryDetails> inventory = (from i in TempTbl
where i.TempId == id
select new InventoryDetails
{
PersonalInventoryBinId = i.PersonalInventoryBinId,
PersonalInventoryGroupId = i.PersonalInventoryGroupId,
}).AsQueryable().ToList().Distinct(new customComparer()).ToList();
public class customComparer:IEqualityComparer<InventoryDetails>
{
public bool Equals(InventoryDetails x, InventoryDetails y)
{
if (x.TempId == y.TempId && x.PersonalInventoryBinId == y.PersonalInventoryBinId
&& x.PersonalInventoryGroupId == y.PersonalInventoryGroupId)
{
return true;
}
return false;
}
public int GetHashCode(InventoryDetails obj)
{
return string.Concat(obj.PersonalInventoryBinId.ToString(),
obj.PersonalInventoryGroupId.ToString(),
obj.TempId.ToString()).GetHashCode();
}
}
As said in a comment by Ivan, you make your life difficult by calling ToList before Distinct. This prevents the SQL provider from incorporating the Distinct call into the generated SQL statement. But that leaves the question: what causes the difference?
The first query generates anonymous type instances. As per the C# specification, by default anonymous types (in C#) are equal when their properties and property values are equal (structural equality). Conversely, by default, reference types (like InventoryDetails) are equal when their reference (say memory address) is equal (reference equality or identity). They can be made equal by overriding their Equals and GetHashcode methods, as some people suggested to do.
But that's not necessary if you remove the first ToList():
var inventory = (from i in _context.TempTbl
where i.TempId == id
select new InventoryDetails
{
PersonalInventoryBinId = i.PersonalInventoryBinId,
PersonalInventoryGroupId = i.PersonalInventoryGroupId,
}).Distinct().ToList();
Now the whole statement until ToList() is an IQueryable that can be translated into SQL. The SQL is executed and the database returns a distinct result set of raw records from which EF materializes InventoryDetails objects. The C# runtime code was even never aware of duplicates!

Why predicate isn't filtering when building it via reflection

I'm building a rather large filter based on an SearchObject that has 50+ fields that can be searched.
Rather than building my where clause for each one of these individually I thought I'd use some slight of hand and try building custom attribute suppling the necessary information and then using reflection to build out each of my predicate statements (Using LinqKit btw). Trouble is, that the code finds the appropriate values in the reflection code and successfully builds a predicate for the property, but the "where" doesn't seem to actually generate and my query always returns 0 records.
The attribute is simple:
[AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
public class FilterAttribute: Attribute
{
public FilterType FilterType { get; set; } //enum{ Object, Database}
public string FilterPath { get; set; }
//var predicate = PredicateBuilder.False<Metadata>();
}
And this is my method that builds out the query:
public List<ETracker.Objects.Item> Search(Search SearchObject, int Page, int PageSize)
{
var predicate = PredicateBuilder.False<ETracker.Objects.Item>();
Type t = typeof(Search);
IEnumerable<PropertyInfo> pi = t.GetProperties();
string title = string.Empty;
foreach (var property in pi)
{
if (Attribute.IsDefined(property, typeof(FilterAttribute)))
{
var attrs = property.GetCustomAttributes(typeof(FilterAttribute),true);
var value = property.GetValue(SearchObject, null);
if (property.Name == "Title")
title = (string)value;
predicate.Or(a => GetPropertyVal(a, ((FilterAttribute)attrs[0]).FilterPath) == value);
}
}
var res = dataContext.GetAllItems().Take(1000)
.Where(a => SearchObject.Subcategories.Select(b => b.ID).ToArray().Contains(a.SubCategory.ID))
.Where(predicate);
return res.ToList();
}
The SearchObject is quite simple:
public class Search
{
public List<Item> Items { get; set; }
[Filter(FilterType = FilterType.Object, FilterPath = "Title")]
public string Title { get; set; }
...
}
Any suggestions will be greatly appreciated. I may well be going way the wrong direction and will take no offense if someone has a better alternative (or at least one that works)
You're not assigning your predicate anywhere. Change the line to this:
predicate = predicate.Or(a => GetPropertyVal(a, ((FilterAttribute)attrs[0]).FilterPath) == value);

Combining 2 lists and and remove duplicates .Output in a third list .My attempts do not work

I always seem to have a problem when I need to compare 2 list and produce a 3rd list which include all unique items.I need to perform this quite often.
Attempt to reproduce the issue with a noddy example.
Am I missing something?
Thanks for any suggestions
The wanted result
Name= Jo1 Surname= Bloggs1 Category= Account
Name= Jo2 Surname= Bloggs2 Category= Sales
Name= Jo5 Surname= Bloggs5 Category= Development
Name= Jo6 Surname= Bloggs6 Category= Management
Name= Jo8 Surname= Bloggs8 Category= HR
Name= Jo7 Surname= Bloggs7 Category= Cleaning
class Program
{
static void Main(string[] args)
{
List<Customer> listOne = new List<Customer>();
List<Customer> listTwo = new List<Customer>();
listOne.Add(new Customer { Category = "Account", Name = "Jo1", Surname = "Bloggs1" });
listOne.Add(new Customer { Category = "Sales", Name = "Jo2", Surname = "Bloggs2" });
listOne.Add(new Customer { Category = "Development", Name = "Jo5", Surname = "Bloggs5" });
listOne.Add(new Customer { Category = "Management", Name = "Jo6", Surname = "Bloggs6" });
listTwo.Add(new Customer { Category = "HR", Name = "Jo8", Surname = "Bloggs8" });
listTwo.Add(new Customer { Category = "Sales", Name = "Jo2", Surname = "Bloggs2" });
listTwo.Add(new Customer { Category = "Management", Name = "Jo6", Surname = "Bloggs6" });
listTwo.Add(new Customer { Category = "Development", Name = "Jo5", Surname = "Bloggs5" });
listTwo.Add(new Customer { Category = "Cleaning", Name = "Jo7", Surname = "Bloggs7" });
List<Customer> resultList = listOne.Union(listTwo).ToList();//**I get duplicates why????**
resultList.ForEach(customer => Console.WriteLine("Name= {0} Surname= {1} Category= {2}", customer.Name, customer.Surname, customer.Category));
Console.Read();
IEnumerable<Customer> resultList3 = listOne.Except(listTwo);//**Does not work**
foreach (var customer in resultList3)
{
Console.WriteLine("Name= {0} Surname= {1} Category= {2}", customer.Name, customer.Surname, customer.Category);
}
**//Does not work**
var resultList2 = (listOne
.Where(n => !(listTwo
.Select(o => o.Category))
.Contains(n.Category)))
.OrderBy(n => n.Category);
foreach (var customer in resultList2)
{
Console.WriteLine("Name= {0}
Surname= {1}
Category= {2}",
customer.Name,
customer.Surname,
customer.Category);
}
Console.Read();
}
}
public class Customer
{
public string Name { get; set; }
public string Surname { get; set; }
public string Category { get; set; }
}
Couldn't you do this by using the Concat and Distinct LINQ methods?
List<Customer> listOne;
List<Customer> listTwo;
List<Customer> uniqueList = listOne.Concat(listTwo).Distinct().ToList();
If necessary, you can use the Distinct() overload that takes an IEqualityComparer to create custom equality comparisons
The crux of the problem is the Customer object doesn't have a .Equals() implementation. If you override .Equals (and .GetHashCode) then .Distinct would use it to eliminate duplicates. If you don't own the Customer implementation, however, adding .Equals may not be an option.
An alternative is to pass a custom IEqualityComparer to .Distinct(). This lets you compare objects in different ways depending on which comparer you pass in.
Another alternative is to GroupBy the fields that are important and take any item from the group (since the GroupBy acts as .Equals in this case). This requires the least code to be written.
e.g.
var result = listOne.Concat(listTwo)
.GroupBy(x=>x.Category+"|"+x.Name+"|"+x.Surname)
.Select(x=>x.First());
which gets your desired result.
As a rule I use a unique delimiter to combine fields so that two items that should be different don't unexpectedly combine to the same key. consider: {Name=abe, Surname=long} and {Name=abel, Surname=ong} would both get the GroupBy key "abelong" if a delimiter isn't used.
The best option is implement the interface IEqualityComparer and use it within Union or Distinct method as I wrote at the end of this article
http://blog.santiagoporras.com/combinar-listas-sin-duplicados-linq/
Implementation of IEqualityComparer
public class SaintComparer : IEqualityComparer<Saint>
{
public bool Equals(Saint item1, Saint item2)
{
return item1.Name == item2.Name;
}
public int GetHashCode(Saint item)
{
int hCode = item.Name.Length;
return hCode.GetHashCode();
}
}
Use of comparer
var unionList = list1.Union(list2, new SaintComparer());
I had a similar problem where I had two very large lists with random strings.
I made a recursive function which returns a new list with unique strings. I compared two lists with 100k random strings(it may or may not exist duplicates) each with 6 characters of abcdefghijklmnopqrstuvwxyz1234567890 and it was done in about 230 ms. I only measured the given function.
I hope this will give value to someone.
Image of test run
makeCodesUnique(List<string> existing, List<string> newL)
{
// Get all duplicate between two lists
List<string> duplicatesBetween = newL.Intersect(existing).ToList();
// Get all duplicates within list
List<string> duplicatesWithin = newL.GroupBy(x => x)
.Where(group => group.Count() > 1)
.Select(group => group.Key).ToList();
if (duplicatesBetween.Count == 0 && duplicatesWithin.Count == 0)
{
// Return list if there are no duplicates
return newL;
}
else
{
if (duplicatesBetween.Count != 0)
{
foreach (string duplicateCode in duplicatesBetween)
{
newL.Remove(duplicateCode);
}
// Generate new codes to substitute the removed ones
List<string> newCodes = generateSomeMore(duplicatesBetween.Count);
newL.AddRange(newCodes);
makeCodesUnique(existing, newL);
}
else if (duplicatesWithin.Count != 0)
{
foreach (string duplicateCode in duplicatesWithin)
{
newL.Remove(duplicateCode);
}
List<string> newCodes = generateSomeMore(duplicatesWithin.Count);
new.AddRange(newCodes);
makeCodesUnique(existing, newL);
}
}
return newL;
}

Sum of hierarchical data using LINQ?

Is it possible to sum hierarchical data using .NET's LINQ?
My data class looks like this:
class Node
{
public decimal Amount;
public IEnumerable<Node> Children { get; set; }
}
So I would have some data looks like this, but the tree could of course be arbitrarily deep.
var amounts = new Node
{
Amount = 10;
Children = new[]
{
new Node
{
Amount = 20
},
new Node
{
Amount = 30
}
}
};
It is possible sum all the amounts and get the result 60 with one simple LINQ query?
You can do it with a higher order function:
Func<Node, decimal> summer = null;
summer = node => node.Amount +
(node.Children == null ? 0m : node.Children.Sum(summer));
decimal total = summer(amounts);
Note that if you can ensure that node.Children will never be null, summer can be simpler:
summer = node => node.Amount + node.Children.Sum(summer);
Alternatively, you could use the null coalescing operator:
summer = node => node.Amount +
(node.Children ?? Enumerable.Empty<Node>()).Sum(summer);
Of course you could put this into a separate method instead:
static decimal SumNodes(Node node)
{
return node.Amount +
(node.Children ?? Enumerable.Empty<Node>())
.Sum((Func<Node, decimal>)SumNodes);
}
Note the ugliness here is due to an ambiguity in method group conversions. Method groups don't get much love in type inference.
and then call SumNodes(amount). Lots of options :)
Full example of the first form:
using System;
using System.Collections.Generic;
using System.Linq;
class Node
{
public decimal Amount;
public IEnumerable<Node> Children { get; set; }
}
public class Test
{
static void Main()
{
var amounts = new Node {
Amount = 10, Children = new[] {
new Node { Amount = 20 },
new Node { Amount = 30 }
}
};
Func<Node, decimal> summer = null;
summer = node => node.Amount +
(node.Children == null ? 0m : node.Children.Sum(summer));
decimal total = summer(amounts);
Console.WriteLine(total);
}
}
I'm not sure I'd call any of these a "simple" LINQ query, mind you...
Technically you can write recursive lambda expressions, but you need to be insane or insanely bright to try (I haven't figured out which). But you can cheat:
Func<Node, decimal> nodeSum = null;
nodeSum = node => {
decimal result = node.Amount;
if (node.Children != null) {
result = result + node.Children.Sum(nodeSum);
}
return result;
};
var value = nodeSum(amounts);

Resources