Currently, I am working on a requirement that uses a List and retrieves the data. But there's change that I need to incorporate which I'm not getting.
Now I'm having a list of elements and on user selection, I would receive the column names(randomly) which are part of that list.
How do I implement because in run-time I don't know how many column names I would receive.
Below is the sample code:
class Program
{
static void Main(string[] args)
{
var students = new List<Student>()
{
new Student { Name = "Vijay", Age = 21 , Gender = "M" , Address = "Address_1"},
new Student { Name = "Ajay", Age = 26 , Gender = "M" , Address = "Address_2"},
new Student { Name = "John", Age = 21 , Gender = "M" , Address = "Address_3"},
new Student { Name = "Rob", Age = 42 , Gender = "M" , Address = "Address_4"},
new Student { Name = "Kohli", Age = 32 , Gender = "M" , Address = "Address_5"}
};
var result = students.Select(x => x.Name);
// I get result with Name column. That's fine.
}
}
public class Student
{
public string Name;
public int Age;
public string Gender;
public string Address;
}
The above code is just a sample of what I'm trying to explain. Since students.Select(x => x.Name); would give me the result which has a List of Names. But some times I might receive comma separated column names
like Name, Gender or Age, Gender or Name, Address or Address or all column names.
Expected results:
// When user sends Name & Gender as selected columns
Name, Gender
vijay 26
Ajay 21
Rob 42
...
// When user sends only Address as selected columns
Address
Address_1
Address_2
....
Can anyone please help me how do I build runtime select using the available list.
Thanks in advance
Dynamic Linq may help you.
using System.Linq.Dynamic.Core;
var values = new List<string> { "Age", "Gender" };
var columns = "new {" + string.Join(",", values) + "}";
var result = students.AsQueryable().Select(columns);
foreach (var x in result)
Console.WriteLine(x);
Output:
{ Age = 21, Gender = M }
{ Age = 26, Gender = M }
{ Age = 21, Gender = M }
{ Age = 42, Gender = M }
{ Age = 32, Gender = M }
Be aware that you are getting an anonymous type. And how you will work with it further is still a question.
I actually did this some time back for a web API. Basically, you can use Newtonsoft.Json to serialize your type while keeping only the fields\properties you want.
First, you'll need a custom ContractResolver:
public class SelectiveContractResolver : DefaultContractResolver
{
private readonly HashSet<string> properties;
public SelectiveContractResolver(HashSet<string> selectedProperties)
{
properties = selectedProperties;
}
protected override JsonProperty CreateProperty
(
MemberInfo member,
MemberSerialization memberSerialization
)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = _ =>
properties
.Contains(
$"{member.ReflectedType.FullName.ToString()}.{member.Name}"
.ToLower());
return property;
}
}
Here I specifically form my strings like "[full path goes here].Student.Name" or "[same here].Student.Gender" and check if property was selected for. This could be useful if you are going to serialize complex types and will need to feed selections for different types to your Resolver.
Second thing to do, and this is purely for optimization, create a factory that caches your resolvers, so you don't have to rebuild them every time:
public class SelectiveContractResolverFactory
{
private ConcurrentDictionary<HashSet<string>, IContractResolver>
cachedResolvers;
public SelectiveContractResolverFactory()
{
cachedResolvers =
new ConcurrentDictionary<HashSet<string>, IContractResolver>
(HashSet<string>.CreateSetComparer());
}
public IContractResolver GetResolver
(
IEnumerable<string> selectedProperties
)
{
var selectedSet = selectedProperties.ToHashSet();
if (cachedResolvers.ContainsKey(selectedSet))
return cachedResolvers[selectedSet];
var newResolver = new SelectiveContractResolver(selectedSet);
cachedResolvers[selectedSet] = newResolver;
return newResolver;
}
}
Finally, lets apply that thing to an object:
var student = new Student { Name = "Vijay", Age = 21 , Gender = "M" , Address = "Address_1"};
var selectedProps = new List<string> {"Student.Name","Student.Age"};
// Obviously, this should be injected as a singleton:
var resolverFactory = new SelectiveContractResolverFactory();
var resolver = resolverFactory.GetResolver(selectedProps);
var selectedResult = JsonConvert.SerializeObject
(
student,
Formatting.None,
new JsonSerializerSettings
{
ContractResolver = resolver
}
);
And just like that, you get a neat json object with the properties you wanted.
But, if you do have to get an actual C# object out of this, then you can deserialize into an ExpandoObject using ExpandoObjectConverter:
dynamic filteredStudent = JsonConvert
.DeserializeObject<ExpandoObject>
(
selectedResult,
new ExpandoObjectConverter()
);
Though, the way the question is phrased, I feel like you are going to be returning this data from an API of sorts, so json would already be perfect for this.
Related
I know that in sql you do something like this
WHERE 'val' IN (field1, field2, field3, field4, ...)
I was wondering if there is a way doing something similar using Linq to entities? The only thing I can think of right now is just to create a giant "or" statement of the fields I want to search over like the following
.where(m =>
m.field1.Contains('val') ||
m.field2.Contains('val') ||
m.field3.Contains('val') ||
m.field4.Contains('val'));
Is there a cleaner way of writing this search or is what I have as good as it gets?
You are not using Contains() properly as pointed out by Theodor Zoulias, because IN on SQL checks equality, while contains would be LIKE on SQL. also you are enclosing your string val with ' instead of ", ' only works for a single character.
Assuming you are trying to retrive "m" where any property has a certain value, you're going to have to use reflection:
First create a method to loop trough an object and match the desired value
public bool FieldSearch(object a, string b)
{
//Get the type of your object, to loop through its properties
Type t = a.GetType();
//loop and check (the loop stops once the first property that matches has been found!)
foreach(PropertyInfo p in t.GetProperties())
{
if(p.GetValue(a).ToString()==b)
{
return true;
}
}
return false;
}
Be careful with GetProperties(), you might want to add BidingAttributes because it retrieves every(public) property.
Now just use your new bool method on your linq: (not a good idea regarding performance depending on the context)
.where(m => FieldSearch(m,"val"))
Although all of this is possible, you probably have an architecture problem, because you are going to lose reference very quick, since this linq query returns any object that has that value on any field; without specifying which field.
There are probably better ways to do what you are trying to do..
You can do
.Where(f => new string[] { f.field1, f.field2, f.field3 }.Any(s => s.Contains("val")));
which have the behavior of the code you posted, or
.Where(f => new string[] { f.field1, f.field2, f.field3 }.Contains("val"));
which check for equality.
But I can't say if it's a good idea regarding performance.
Here is an example of the code:
public class ClassWithFields
{
public int Id { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 {get;set;}
}
public class Program
{
public static void Main()
{
var listFields = new List<ClassWithFields>()
{
new ClassWithFields { Id = 1, Field1 = "val", Field2 = "qewr", Field3 = "asdqw" },
new ClassWithFields { Id = 2, Field1 = "asdf", Field2 = "asdd", Field3 = "asdqw" },
new ClassWithFields { Id = 3, Field1 = "asdf", Field2 = "qewr", Field3 = "qwvaleqwe" }
};
var containsVal = listFields.Where(f => new string[] { f.Field1, f.Field2, f.Field3 }.Any(s => s.Contains("val")));
var equalsVal = listFields.Where(f => new string[] { f.Field1, f.Field2, f.Field3 }.Contains("val"));
}
}
You can run it at https://dotnetfiddle.net/lXSoB4
I have a client list, it internally contains list of phone numbers, i need to filter the list of client which matches the client phone number from another list of phonenumbers using linq.
Here is my code
My client object is
public class Client
{
public int id;
public string firstname;
public string lastname;
public List<phone> phones;
//etc.
}
public class phone
{
public int id;
public string phoneno;
}
var searchByPhone = new List<string,string>();
searchByPhone.Add("12324344", "message one");
searchByPhone.Add("45646565", "message two");
searchByPhone.Add("56868675", "message three");
//first one is phone number and second is the text message.
Here my need is
I need to list the clients by searchByPhone phonenumber from client phones list and merge the result set to new Clientobject mentioned below. any help please?
public class ClientObject
{
public int id;
public string firstname;
public string lastname;
public string phonenumber;
public string message;
}
Thanks in advance.
Something like this should do the trick:
var searchByPhone = new List<Tuple<string,string>>();
searchByPhone.Add(Tuple.Create("12324344", "message one"));
searchByPhone.Add(Tuple.Create("45646565", "message two"));
searchByPhone.Add(Tuple.Create("56868675", "message three"));
//you should already have this list populated somehow, i'm declaring it here just for the sake of making the code compile
var clientlist = new List<Client>();
//this list will hold your results
List<ClientObject> resultList = new List<ClientObject>();
searchByPhone.ForEach(tp => resultList.AddRange(
clientlist.Where(cl => cl.phones.Any(ph=>ph.phoneno == tp.Item1)).Select(cl => new ClientObject {id = cl.id, firstname = cl.firstname, lastname = cl.lastname, message = tp.Item2, phonenumber = tp.Item1}).ToList()));
Note: you either need to make the members of client, clientobject and phone classes public, or preferably create public properties to get/set their values.
This will give you a list of ClientObject objects, which contain the id, first and last name from the Client object as well as the appropraite number and message from the searchByPhone tuples.
I think that is what you were after. If not, please clarify your needs and I can adjust the answer
Try something like:
var data = (from dt in searchByPhone
let cl = clientList.Where(z => z.phones.Any(y => y.phoneno.Equals(dt.Item1)))
select cl.Select(x => new ClientObject
{
firstname = x.firstname,
lastname = x.lastname,
id = x.id,
phonenumber = dt.Item1,
message = dt.Item2
})).Aggregate((a, b) =>
{
a.ToList().AddRange(b);
return a;
});
I have two lists of the same object and I want to find the Union and Intersection of these lists based on a case-insensitive comparison of a property of the object.
For simplicity, let's call it a Person, and I want to filter on the Person.Name property.
What is the recommended way to do this? I'm hoping to keep the code in a single line of Linq.
Currently I'm doing the following:
public class Person { public string Name { get; set; } }
-
var people =
firstListOfPeople.Where(
p1 => p1.Name != null &&
secondListOfPeople
.Where(p2 => p2.Name != null)
.Select(p2 => p2.Name.ToUpper())
.Contains(p1.Name.ToUpper()));
You can collapse your code down to this:
firstListOfPeople.Intersect(secondListOfPeople);
The catch comes with the case-insensitive compare of the name. Intersect uses the default equality comparer (reference equality), so you need to implement IEqualityComparer<T> (MSDN).
That comparison would do the name based comparison. You would then create one and pass it to the correct overload of Intersect: http://msdn.microsoft.com/en-us/library/vstudio/bb355408(v=vs.100).aspx
firstListOfPeople.Instersect(secondListOfPeople, myComparer);
I think #BradleyDotNET has the right answer, but since I already had an example mostly complete, I thought I'd post it in case it helps someone down the road:
void Main()
{
var firstListOfPeople = new[]
{
new Person { Name = "Rufus" },
new Person { Name = "Bob" },
new Person { Name = "steve" },
};
var secondListOfPeople = new[]
{
new Person { Name = "john" },
new Person { Name = "Bob" },
new Person { Name = "rufus" },
};
var people = firstListOfPeople.Intersect(secondListOfPeople, new PersonNameComparer());
people.Dump(); // displays the result if you are using LINQPad
}
public class Person
{
public string Name { get; set; }
}
public class PersonNameComparer: EqualityComparer<Person>
{
public override bool Equals(Person p1, Person p2)
{
return p1.Name.Equals(p2.Name, StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode(Person p)
{
return p.Name.ToLower().GetHashCode();
}
}
I'm new to C# & having trouble wi/ this current assignment. I need to use a Delegate to sort Sort the employees by social security number in descending order and by last name in ascending order. If anyone can just point me in how to get started it will help greatly. This is just the employee class, but if needed, I can post all the classes.
using System;
// Fig. 12.4: Employee.cs
// Employee abstract base class.
using System.Text;
public abstract class Employee : IPayable
{
// read-only property that gets employee's first name
public string FirstName { get; private set; }
// read-only property that gets employee's last name
public string LastName { get; private set; }
// read-only property that gets employee's social security number
public string SocialSecurityNumber { get; private set; }
// three-parameter constructor
public Employee( string first, string last, string ssn )
{
FirstName = first;
LastName = last;
SocialSecurityNumber = ssn;
} // end three-parameter Employee constructor
// return string representation of Employee object, using properties
public override string ToString()
{
return string.Format( "{0} {1}\nsocial security number: {2}",
FirstName, LastName, SocialSecurityNumber );
} // end method ToString
// abstract method overridden by derived classes
public abstract decimal GetPaymentAmount(); // no implementation here
} // end abstract class Employee
// Fig. 12.15: PayableInterfaceTest.cs
// Tests interface IPayable with disparate classes.
using System;
public class PayableInterfaceTest
{
public static void Main( string[] args )
{
// create four-element IPayable array
IPayable[] payableObjects = new IPayable[8];
// populate array with objects that implement IPayable
payableObjects[0] = new SalariedEmployee("John", "Smith", "111-11-1111", 700M);
payableObjects[1] = new SalariedEmployee("Antonio", "Smith", "555-55-5555", 800M);
payableObjects[2] = new SalariedEmployee("Victor", "Smith", "444-44-4444", 600M);
payableObjects[3] = new HourlyEmployee("Karen", "Price", "222-22-2222", 16.75M, 40M);
payableObjects[4] = new HourlyEmployee("Ruben", "Zamora", "666-66-6666", 20.00M, 40M);
payableObjects[5] = new CommissionEmployee("Sue", "Jones", "333-33-3333", 10000M, .06M);
payableObjects[6] = new BasePlusCommissionEmployee("Bob", "Lewis", "777-77-7777", 5000M, .04M, 300M);
payableObjects[7] = new BasePlusCommissionEmployee("Lee", "Duarte", "888-88-888", 5000M, .04M, 300M);
Console.WriteLine(
"Lab 2 output:\n" );
// generically process each element in array payableObjectsWW
foreach ( var currentPayable in payableObjects )
{
// output currentPayable and its appropriate payment amount
Console.WriteLine( "payment due {0}: {1:C}\n",
currentPayable, currentPayable.GetPaymentAmount() );
} // end foreach
} // end Main
} // end class PayableInterfaceTest
Why don't you use LINQ ?
var sortedArray = payableObjects
.OrderByDescending(e => e.SocialSecurityNumber)
.ThenBy(e => e.LastName)
.ToArray();
Array.Sort(T[], delegate int(T, T)) is probably what you're looking for.
Call Array.Sort with your array as the first parameter, and create a function in your Employee that fills delegate int(Employee, Employee). If I recall correctly, that function needs to return 0 if they are equal, a negative number if the first comes first ascending, and a positive number if the first comes second ascending.
//to order by SSN desc
foreach (var e in payableObjects.OrderByDescending(e => e.SocialSecurityNumber))
{
Console.WriteLine(e.FirstName + " " + e.LastName + " " + e.SocialSecurityNumber);
}
Console.WriteLine("");
//to order by Lastname Asc
foreach (var e in payableObjects.OrderBy(e => e.LastName))
{
Console.WriteLine(e.FirstName + " " + e.LastName + " " + e.SocialSecurityNumber);
}
I want to select columns dynamically from List as following. So what could be the best way?
//a objects list
List<DashBoard> dashboardlist = (List<DashBoard>)objList;
string strColumns = "RecDate,ModifiedDate";
objList = (from obj in dashboardlist select new { strColumns }).ToList();
/////////////
Ok,Just forget Object List say I have database table which have number of column ID,Name,Age,sex,etc ..Then I have columnList to display and the columnList is change according to condition . SO I have List people; and List columnTemplate; so now I want to select the column based on the template .
Thanks for providing ideas to my question.Spending couple of hours in
Google I found solution .
public void Test() {
var data = new[] {
new TestData { X = 1, Y = 2, Z = 3 }
, new TestData { X = 2, Y = 4, Z = 6 }
};
var strColumns = "X,Z".Split(',');
foreach (var item in data.Select(a => Projection(a, strColumns))) {
Console.WriteLine("{0} {1}", item.X, item.Z);
}
}
private static dynamic Projection(object a, IEnumerable<string> props) {
if (a == null) {
return null;
}
IDictionary<string,object> res = new ExpandoObject();
var type = a.GetType();
foreach (var pair in props.Select(n => new {
Name = n
, Property = type.GetProperty(n)})) {
res[pair.Name] = pair.Property.GetValue(a, new object[0]);
}
return res;
}
class TestData {
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}
I assume that the list of the columns may come from an external resource and change, I propose:
With reflection you could produce a list of FieldInfo that correspond to each Column, then loop over each item on the list and each FieldInfo and call GetValue on the data object.
Here is the solution:
Select a Column Dynamically using LINQ?
and look Dynamic Linq: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Let's supposed that you have only 2 templates. You can create a method for each template, that returns only the columns you need. Something like this:
// method for template 1 - returns only 3 columns/properties
private DashBoard CreateDashBoardXxxxxxx(DashBoard item)
{
return new DashBoard {
Property1 = item.Property1,
Property4 = item.Property2,
Property3 = item.Property3
};
}
// method for template 2 - returns N columns/properties
private DashBoard CreateDashBoardYyyyyyyy(DashBoard item)
{
return new DashBoard {
Property1 = item.Property1,
Property4 = item.Property2,
// Other properties
// .....
PropertyN = item.PropertyN
};
}
You can then use those methods like this:
List<DashBoard> dashboardlist = (List<DashBoard>)objList;
// using template 1
var list = dashboardlist.Select(CreateDashBoardXxxxxxx);
// using template 2
var list2 = dashboardlist.Select(CreateDashBoardYyyyyyyy);
You just need to do some code to decide which template should be used.
I hope this helps!!