Convert object to string using Linq - c#

I am trying to convert object to a string using SelectMany. However when list property value is empty then I am not getting desired string.
I am expecting results like
name1-2-4 : name2-
but getting this result as
name1-2-4
. The second name is ignored because of "Scores" list which is empty.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var person1 = new Person()
{
Name = "name1",
Scores = new List<Score>
{
new Score
{
InitialScore =2,
UpdatedScore = 4
}
}
};
var person2 = new Person()
{
Name = "name2",
Scores = new List<Score>()
};
var persons = new List<Person>();
persons.Add(person1);
persons.Add(person2);
var result = string.Join(" : ", persons.SelectMany(x=>x.Scores, (parent, child)=> parent.Name + "-" + child.InitialScore +"-"+ child.UpdatedScore));
Console.WriteLine(result);
}
}
public class Person
{
public string Name {get; set;}
public List<Score> Scores {get; set;}
}
public class Score
{
public int InitialScore {get; set;}
public int UpdatedScore {get; set;}
}
Edit:
Based on #JonasH solution use this linq query.
var result = string.Join(" : ", persons.SelectMany(x=>GetNames(x)));

A simple solution would be to add a helper method to your person that does what you want:
public static IEnumerable<string> GetNames(this Person p)
{
if (p.Scores.Count == 0)
{
yield return p.Name;
}
else
{
foreach (var score in p.Scores)
{
yield return $"{p.Name}-{score}";
}
}
}
and use that instead of .Scores in SelectMany.

var result1 = persons.SelectMany(x => x.Scores, (parent, child) => parent.Name + "-" + child.InitialScore + "-" + child.UpdatedScore).FirstOrDefault();
var result2 = persons.Where(x => x.Scores.Count() <= 0).Select(x => x.Name).FirstOrDefault()+"-";
var result = $"{result1}:{result2}";

Related

Find two values in hierarchical object tree C# using Linq

I have the following structures:
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var tourn = new Tournament();
var player = new Player() { Type = PlayerType.User };
var seat1 = new Seat() { Number = 1, Player = player } ;
tourn.Tables = new List<Table>() { new Table(){ Seats = new List<Seat>(){seat1} } };
//Console.WriteLine(tourn.Tables.Where((k=> k.Tables.Any(m=> m.Seats.Any(j=> j.Player == Player.User)))).Count());
// Get Table and Seat numbers for PlayerType.User
}
public class Seat{
public Player? Player {get;set;}
public int Number {get;set;}
}
public enum PlayerType {
User,
Bot
}
public class Tournament{
public List<Table> Tables {get;set;}
}
public class Table
{
public List<Seat> Seats {get;set;}
public int Number {get;set;}
}
public class Player
{
public PlayerType Type { get; set; }
}
}
I have want the table and seat number where the playertype = user
Is it possible to build a Linq query to do this in one statement?
(I have looked at many examples and can't seem to get it right)
Tried this:
var playerLocation = tables
.Select(seat => new
{
TableNumber = seat.TableNumber,
TableSeat = seat.Seats
.Where(s => s.Player is not null &&
s.Player.Type == PlayerType.User)
.Select(st => new
{
SeatNumber = st.Number
})
});
Fiddle (.NET Core): https://dotnetfiddle.net/CMjZs6
I cheated...
PlayerLocation location = new PlayerLocation();
foreach (Table table in tables)
foreach (Seat seat in table.Seats)
if (seat.Player is not null && seat.Player.Type == PlayerType.User)
{
location.TableNumber = table.TableNumber;
location.SeatNumber = seat.Number;
break;
}
Assuming you want the answer as an object with the table number and a list of seat numbers, it is straightforward:
var ans = tourn.Tables.Select(t => new {
t.Number,
Seats = t.Seats.Where(s => s.Player?.Type == PlayerType.User)
.Select(s => s.Number)
.ToList()
});
Based On your Updated Fiddle
var anonyType = tourn.Tables.Where(k=> k.Seats.Any(j=> j.Player?.Type == PlayerType.User)).Select(k=> new { TableNumber = k.Number, SeatNumbers = k.Seats.Select(j=> j.Number) }).FirstOrDefault();
Console.WriteLine(anonyType.TableNumber);
Console.WriteLine(String.Join(" , ", anonyType.SeatNumbers));

LINQ to Get All heirerichal children

I have been digging this quite a while.
public class Person
{
public string Name { get; set; }
public string Age { get; set; }
public List<Person> Children { get; set; }
}
I want a single LINQ query to find out "All the persons whose Age > 4 in this collection".
Note: You have to traverse Collection of Person + Collection of Children, so each children object will have a collection of Person till Children becomes null.
First i can't understand why all your properties private and Age is not int type. So my class looks like this:
public partial class Person
{
public string Name { get; set; }
public int Age { get; set; }
public List<Person> Childrens { get; set; }
}
Note partial word. This word will allow you to place your class logic in separate files and this could be usefull when you creating some custom logic in class.
Then I simply create this method in different file:
public partial class Person
{
public Person GetPersonWithChindren(int maxAge)
{
return new Person
{
Age = this.Age,
Name = this.Name,
Childrens = this.Childrens != null
? this.Childrens
.Where(x => x.Age < maxAge)
.Select(x => x.GetPersonWithChindren(maxAge)) //this line do recursive magic
.ToList()
: null
};
}
}
As you can see this method checking Age of each child and if Age is ok then it checks next level of hierarchy untill Childrens is null.
So you can use it like this:
var person = new Person()
{
//initialisation of your collection here
}
//result will contains only nodes where Person have age < 4 and Childs that have age < 4
var result = person.GetPersonWithChindren(4);
Note that this solution will work normal with linqToEntities. But if you using LinqToSQL this expression produces query to DB on each Person entity. So if you have many Persons and deep hierarhy it will costs you a lot of machine time. In that case you should to write stored procedure with CTE instead of LinQ query.
UPDATE:
You even can write more general solution with a help of Func<T> class like this:
public partial class Person
{
public Person GetPersonWithChindren(Func<Person, bool> func)
{
return new Person
{
Age = this.Age,
Name = this.Name,
Childrens = this.Childrens != null
? this.Childrens
.Where(x => func(x))
.Select(x => x.GetPersonWithChindren(func))
.ToList()
: null
};
}
}
And then you can use it like this:
var result = person.GetPersonWithChindren(x => x.Age < 4);
You always can change your criteria now where you want to use your function.
Create a visitor. In this example by implementing a helper class:
public static class Helpers
public static IEnumerable<Person> GetDescendants(this Person person)
{
foreach (var child in person.Children)
{
yield return child;
foreach (var descendant in child.GetDescendants())
{
yield return descendant;
}
}
}
this is one of the times where the "yield return many" would be useful.
If you're ensuring that .Children is automatically created, then this works:
Func<Person, Func<Person, bool>, Person> clone = null;
clone = (p, f) => f(p) ? new Person()
{
Name = p.Name,
Age = p.Age,
Children = p.Children.Select(c => clone(c, f)).Where(x => x != null).ToList(),
} : null;
var olderThan4 = clone(person, p => p.Age > 4);
Yes, that's it. Effectively three lines.
If you start with this data:
var person = new Person()
{
Name = "Fred", Age = 30,
Children = new List<Person>()
{
new Person() { Name = "Bob", Age = 7, },
new Person() { Name = "Sally", Age = 3, }
},
};
...then you get this result:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
private List<Person> _children = null;
public List<Person> Children
{
get
{
if (_children == null)
{
_children = new List<Person>();
}
return _children;
}
set
{
_children = value;
}
}
}

Subtract two properties in list with eachother

I want to get the difference between two integers, in this case "Age" - subtract them.
Here my class and my list. I want to, with a method, take the age from Robin and Sara and show the age difference. Is this possible with LINQ or..?
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public class LinqQuery
{
private readonly List<Person> _persons = new List<Person>
{
new Person {FirstName = "Robin", LastName = "Blixt", Age = 29},
new Person {FirstName = "Sara", LastName = "Johansson", Age = 44}
};
public IEnumerable<Person> GetAll()
{
return _persons;
}
public void difference()
{
?????
Console.ReadKey();
}
}
Thanks in advance.
You can use lambda expression to find specified index if FirstName is youre key and you have more items than two.
Please note that I did not check any erros (empty list etc.)
public void diff()
{
int indxRobin = lst.FindIndex((item) => { return item.FirstName == "Robin"});
int indxSara = lst.FindIndex((item) => { return item.FirstName == "Sara"});
return lst[indxRobin].Age - lst[indxSara].Age;
}
Using a cross-join you could calculate the age difference for all permutations in the list.
Of course, this is very crude and gives all duplicates, but it's easy from there to remove the duplicates in the query.
public void Difference()
{
var ageDifferences = from p1 in _persons
from p2 in _persons
select new
{
P1FullName = p1.FirstName + " " + p1.LastName,
P2FullName = p2.FirstName + " " + p2.LastName,
AgeDifference = Math.Abs(p1.Age - p2.Age)
};
foreach (var item in ageDifferences)
{
Console.Out.WriteLine("{0} and {1} have {2} years of age difference.", item.P1FullName, item.P2FullName, item.AgeDifference);
}
Console.ReadKey();
}
Thanks, #Tim Schmelter for the suggestion :)
public void difference()
{
int sara= _persons.FirstOrDefault(p=>p.FirstName=="Sara").Age;
int robin=_persons.FirstOrDefault(p=>p.FirstName=="Robin").Age;
int difference= Math.abs(sara-robin);
Console.ReadKey();
}

How to make select query by string property name in lambda expression?

I would like to make a query by using lambda select,
Like below:
public class Foo{
public int Id {get;set;}
public string Name {get;set;}
public string Surname {get;set;}
}
var list = new List<Foo>();
var temp = list.Select(x=> x("Name"),("Surname"));
The property name needs to be sent as a string,
I dont know how to use, I have given it for being a example.
is it possible?
Edit:
Foo list :
1 A B
2 C D
3 E F
4 G H
I don't know type of generic list, I have property name such as "Name", "Surname"
I want to be like below:
Result :
A B
C D
E F
G H
The following code snippet shows 2 cases. One filtering on the list, and another creating a new list of anonymous objects, having just Name and Surname.
List<Foo> list = new List<Foo>();
var newList = list.Select(x=> new {
AnyName1 = x.Name,
AnyName2 = x.Surname
});
var filteredList = list.Select(x => x.Name == "FilteredName" && x.Surname == "FilteredSurname");
var filteredListByLinq = from cust in list
where cust.Name == "Name" && cust.Surname == "Surname"
select cust;
var filteredByUsingReflection = list.Select(c => c.GetType().GetProperty("Name").GetValue(c, null));
Interface
If you have access to the types in question, and if you always want to access the same properties, the best option is to make the types implement the same interface:
public interface INamable
{
string Name { get; }
string Surname { get; }
}
public class Foo : INamable
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
This will preserve type safety and enable queries like this one:
public void ExtractUsingInterface<T>(IEnumerable<T> list) where T : INamable
{
var names = list.Select(o => new { Name = o.Name, Surname = o.Surname });
foreach (var n in names)
{
Console.WriteLine(n.Name + " " + n.Surname);
}
}
If, for some reason, you can't alter the original type, here are two more options.
Reflection
The first one is reflection. This is Mez's answer, i'll just rephrase it with an anonymous type like in the previous solution (not sure what you need exactly):
public void ExtractUsingReflection<T>(IEnumerable<T> list)
{
var names = list.Select(o => new
{
Name = GetStringValue(o, "Name"),
Surname = GetStringValue(o, "Surname")
});
foreach (var n in names)
{
Console.WriteLine(n.Name + " " + n.Surname);
}
}
private static string GetStringValue<T>(T obj, string propName)
{
return obj.GetType().GetProperty(propName).GetValue(obj, null) as string;
}
Dynamic
The second uses dynamic:
public void ExtractUsingDynamic(IEnumerable list)
{
var dynamicList = list.Cast<dynamic>();
var names = dynamicList.Select(d => new
{
Name = d.Name,
Surname = d.Surname
});
foreach (var n in names)
{
Console.WriteLine(n.Name + " " + n.Surname);
}
}
With that in place, the following code:
IEnumerable<INamable> list = new List<Foo>
{
new Foo() {Id = 1, Name = "FooName1", Surname = "FooSurname1"},
new Foo() {Id = 2, Name = "FooName2", Surname = "FooSurname2"}
};
ExtractUsingInterface(list);
// IEnumerable<object> list... will be fine for both solutions below
ExtractUsingReflection(list);
ExtractUsingDynamic(list);
will produce the expected output:
FooName1 FooSurname1
FooName2 FooSurname2
FooName1 FooSurname1
FooName2 FooSurname2
FooName1 FooSurname1
FooName2 FooSurname2
I'm sure you can fiddle with that and get to what you are trying to achieve.
var temp = list.Select(x => x.Name == "Name" && x.Surname == "Surname");
var temp = list.Select(x => new {Name = x.Name, Surname = x.Surname});

Dynamic linq OrderBy isn't working for me

I am having trouble dynamically specifying the column and direction to sort on.
I currently have the following code:
if (sort != "OrderID")
{
if (sort == "EmployeeName")
{
sort = "Employee.FirstName"; //sort by Employee FirstName
}
else
{
sort = "Customer." + sort; //Customer.CompanyName sort
}
}
var sortCriteria = string.Format("{0} {1}", sort, sortDir);
var res1 = nwd.Orders //response
.OrderBy(o => sort+" "+sortDir)
.ThenBy(o => o.OrderID)
.Skip((page - 1) * rowsPerPage)
.Take(rowsPerPage)
.Select(o => new
{
o.OrderID,
o.Customer.CompanyName,
o.Customer.ContactName,
o.Employee.FirstName,
o.Employee.LastName,
o.Order_Details
}).ToList();
Any help would be much appreciated!
How about this?
var res1 = nwd.Orders //response
IOrderedQueryable<Orders> result;
if (sort != "OrderID")
{
if (sort == "EmployeeName")
{
result = res1.OrderBy(o => o.Employee.FirstName);
}
else
{
result= res1.OrderBy(o => o.Customer.CompanyName);
}
}
result = result.ThenBy(o => o.OrderID)
.Skip((page - 1) * rowsPerPage)
.Take(rowsPerPage)
.Select(o => new
{
o.OrderID,
o.Customer.CompanyName,
o.Customer.ContactName,
o.Employee.FirstName,
o.Employee.LastName,
o.Order_Details
}).ToList();
Ok, here's a working example with a dummy class:
internal class Program
{
private static void Main(string[] args)
{
var list_people = new List<Person> {
new Person() {Age = 4, Name = "yo"},
new Person() {Age = 5, Name = "a"}
};
var dynamic_propretry = typeof (Person).GetProperty("Name");
var sorted = list_people.OrderBy(person => dynamic_propretry.GetValue(person, null));
foreach (var person in sorted)
{
Console.Out.WriteLine(person);
}
Console.ReadLine();
}
}
public class Person{
public int Age { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name + ":" + Age;
}
}
So, all you have to do is store the property you want in a string, and just use that string where I used "Name". It should work.

Categories