There is a class for passengers which contain string name, int age, string job etc. I made an array of this class, lets say it has 10 places.
I wanna find the oldest passenger.
My code doesn't work, because it is not possible to compare passenger[i] with an integer. I mean I need only the age in passenger[]
How to find the oldest one in passenger[]?
EDIT: The return value should be a passenger by his name and major, not only its age.
public Passenger Oldest()
{
int oldest = 0;
for (int i = 0; i < passengers.Length; i++)
{
if (passengers[i] > oldest)
{
oldest = passengers[i];
}
}
return oldest;
}
class Passenger
{
int age;
string name;
string major;
public Passenger(int _age, string _name, string _major)
{
age = _age;
name = _name;
major = _major;
}
}
Firstly, as mentioned by #Cid in comment to question all fields of Passenger are private (by default when modifier is not specified). You should either mark then as public (to access them outside declaring class) or better create public properties:
class Passenger
{
public int Age { get; set; } // public auto property
public string Name { get; set; } // public auto property
public string Major { get; set; } // public auto property
public Passenger(int age, string name, string major)
{
Age = age;
Name = name;
Major = major;
}
}
Secondly, you need to compare Age (proeprty, not whole object) of the passenger:
if (passengers[i].Age > oldest)
{
oldest = passengers[i].Age;
}
Also, you could use LINQ to find the oldest passenger:
var oldest = passengers.Max(item => item.Age);
Finally, to return the oldest passenger:
public Passenger Oldest()
{
// if no passengers -> return null
if (!passengers?.Any() ?? true)
{
return null;
}
var maxAge = passengers.Max(item => item.Age);
return passengers.First(item => item.Age == maxAge);
}
Also, as mentioned by #DmitryBychenko the method can be shortened to:
public Passenger Oldest()
{
// if no passengers -> return null
if (!passengers?.Any() ?? true)
{
return null;
}
return passengers.Aggregate((s, a) => s.Age > a.Age ? s : a);
}
or without LINQ:
public Passenger Oldest()
{
// if no passengers -> return null
if (passengers == null || passengers.Length == 0)
{
return null;
}
var maxAge = passengers[0].Age;
var oldestPassengerIndex = 0;
for (var i = 1; i < passengers.Length; i++)
{
if (passengers[i].Age > maxAge)
{
oldest = passengers[i].Age;
oldestPassengerIndex = i;
}
}
return passengers[oldestPassengerIndex];
}
Slightly more efficient version of Roman's answer, especially if the oldest passenger appears last in the list:
public Passenger Oldest()
{
if (passengers.Length == 1) return passengers[0];
var oldest = passengers[0];
for (int i = 1; i < passengers.Length; i++)
{
if (passengers[i].Age > oldest.Age) oldest = passengers[i];
}
return oldest;
}
This only ever requires a single iteration.
Using Linq is easy
var oldest=passengers.Max(x=>x.Age):
Otherwise
Passenger oldest=new Passenger(0,"","");
foreach (Passenger p in Passengers){
if (p.age>oldest.age) oldest=p;
}
Related
I have a list of object like below
public class Person
{
public string Name {get; set;}
public int Age {get; set;}
}
public class SomeClass
{
public int DoSomething ()
{
int result;
List<Person> personList = new List<Person>();
personList.Add(new Person { //with 1 object, just to keep simple
Name = "Someone",
Age = 18});
Person eighteenYearsOld = _checkAge.FindEighteenYearsOld (personList);
int index = personList.IndexOf (eighteenYearsOld);
//do something
return result;
}
}
[TestMethod]
public void DoSomething_Test()
{
//Given:
//When: I call SomeClass object
Person eightnneYears = new Person {
Name = "Someone",
Age = 18};
_mockCheckAge.Setup (x => x.FindEighteenYearsOld(It.IsAny<List<Person>>())).Returns(eightnneYears);
_someClass = new SomeClass (_mockCheckAge.Object);
int result = _someClass.DoSomething();
//Then:
}
As I have mocked the FindEighteenYearsOld method it returns a Person object with same states which is presents in the personList. But when personList.IndexOf() executes it returns index -1, which is suppose to be 0. What should I do.
List.IndexOf uses Equals to find equal objects. If your class doesn't override it only references are compared. Since your two instances are not the same List.IndexOf returns -1.
So override Equals (and also always GetHashCode then):
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override bool Equals (Object obj)
{
Person otherPerson = obj as Person;
return otherPerson != null
&& otherPerson.Name == Name
&& otherPerson.Age == Age;
}
// http://stackoverflow.com/a/263416/284240
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
hash = hash * 23 + (Name?.GetHashCode() ?? 0); // or: hash = hash * 23 + (Name == null ? 0 : Name.GetHashCode());
hash = hash * 23 + Age; ;
return hash;
}
}
}
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();
}
I have a class Person which contains several properties. Multiple instances of this class are in an array PersonList.
Is it possible to compare properties of different instances of an object in an array?
For example : find out who is the oldest and who is the youngest ?
public class Persoon
{
public Persoon()
{
}
//Properties maken
public Persoon(string naam, Int16 gewicht, Int16 lengte, DateTime geboortedatum)
{
this.Naam = naam;
this.Gewicht = gewicht;
this.Lengte = lengte;
this.Geboortedatum = geboortedatum;
}
public string Naam { get; set; } // AutoProperty
public double Gewicht { get; set; } // AutoProperty
public int Lengte { get; set; } // AutoProperty
public DateTime Geboortedatum { get; set; } // AutoProperty
public double BerekenBmi()
{
//BMI formule: Gewicht in kilogram / (Lengte in meter * Lengte in meter)
return Math.Round(Gewicht/((Lengte/100.0) * (Lengte/100.0)),1);
}
public string BmiStadiumBerekenen()
{
if (BerekenBmi() < 18.5) return "ondergewicht";
if (BerekenBmi() >= 18.5 && BerekenBmi() <= 24.9) return "normaal";
if (BerekenBmi() >= 25 && BerekenBmi() <= 29.9) return "overgewicht";
if (BerekenBmi() >= 30 && BerekenBmi() <= 34.9) return "obesitas I";
if (BerekenBmi() >= 35 && BerekenBmi() <= 39.9) return "obesitas II";
else return "morbide obesitas";
}
public int BerekenLeeftijd()
{
TimeSpan leeftijd = DateTime.Today - Geboortedatum;
return (int) leeftijd.TotalDays;
}
}
I need to compare the oldest and the youngest person so i can calculate the amount of time that is between them.. I've done this before with only 2 persons without arrays but now i need to do it with them.
Of course you can, and the solution is in your question! You have to use a compareTo() method.
You have to implement the compareTo() method in the class Person.
public class Person implements Comparable<Person>{...}
then you simply add the method
#Override
public int compareTo(Person p) {...}
This method want another Person object in the parameters, so you can choose the way to compare the people. For example you said that you want to compare the age of each Person, you have just to put an if close sentence inside the compareTo() method.
int result;
if (this.age < p.getAge()) {
esito = -1;
}
else if(this.age = p.getAge()){
esito = 0;
}
else{
esito = 1;
}
return esito;
This method returns an int number: a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
Source:
http://docs.oracle.com/javase/7/docs/api/
School lessons
:)
Possibly something like this (using Linq):
var youngest = personList.OrderBy(p => p.Age).First();
(Noting that this assumes that there is seomthing in your list to start with).
I have a situation, where I get data from the database in such a way, that everything is stored in one-dimensional array.
For example:
User table: UserId, Name
Group table: GroupId, Name
UserGroup table: UserId, GroupId
As a result of joining these tables I obtain array of the following form:
result[0] = "1" // user id
result[1] = "John Doe" // user name
result[2] = "121" // group id
result[3] = "SomeGroup" // group name
result[4] = "1" // user id
result[5] = "John Doe" // user name
result[6] = "2135" // group id
result[7] = "SomeOtherGroup" // group name
I know it's not a good solution of keeping data, but these data are coming to me from some other piece of code which I am not allowed to change, so I have to deal with it.
My questions are:
Is this possible to use LINQ in order to parse this array and place data in my own objects (User class with Groups collection in it).
What is other best way to handle it if not by LINQ?
Pure linq Expression :
int i = 0;
var objects = result.GroupBy(x => Math.Floor(i++ / 4.0))
.Select(g => new { id =g.ElementAt(0), name = g.ElementAt(1), gId= g.ElementAt(2), group = g.ElementAt(3)})
.GroupBy(x=>new {x.id, x.name}, x=>new {x.gId, x.group})
.Select(y=>new {y.Key, groups = y.ToList()});
In the first GroupBy I group results in 4 elements subsets using a floor and a temporary variable.
Then The next Select put the resulting arrays in an anonymous type for better usability in the next steps.
The next GroupBy is used to group the entries by Employee. The Key will be the employee and the values will be the corresponding Groups.
Finaly the lase Selectis used to put the GroupByresult in a better shape. I choose to put the result in an other anonymous type but You could instantiate you custom objects here and put the values in the right fields using curly brace constructor.
If your logic depends on indexes LINQ is is rarely the right tool. It results in less readable, maintainable, efficient and robust code than with plain loops.
I would use something like following to create two dictionaries representing the many to many relation. Note the for-loop which increments by 4 on every iteration since that seems to be the user-group-"package":
var userIdGroups = new Dictionary<int, HashSet<Group>>();
var groupIdUsers = new Dictionary<int, HashSet<User>>();
for(int i = 0; i < result.Length; i += 4)
{
int id;
if(int.TryParse(result[i], out id))
{
string name = result.ElementAtOrDefault(i + 1);
if(name == null)
continue; // end, invalid data
User user = new User{ UserId = id, Name = name };
string groupID = result.ElementAtOrDefault(i + 2);
if(!int.TryParse(groupID, out id))
continue; // end, invalid data
name = result.ElementAtOrDefault(i + 3);
if(name == null)
continue; // end, invalid data
Group group = new Group{ GroupId = id, Name = name };
HashSet<Group> userGroups;
HashSet<User> groupUsers;
if (userIdGroups.TryGetValue(user.UserId, out userGroups))
userGroups.Add(group);
else
userIdGroups.Add(user.UserId, new HashSet<Group>{ group });
if (groupIdUsers.TryGetValue(group.GroupId, out groupUsers))
groupUsers.Add(user);
else
groupIdUsers.Add(group.GroupId, new HashSet<User> { user });
}
}
The result is:
the user-dictionary contains one user with two groups
the group-dictionary contains two groups which map to the same user
You have to override Equals and GetHashCode to compare the ID's:
class User
{
public int UserId { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
User u2 = obj as User;
if (u2 == null) return false;
return UserId == u2.UserId;
}
public override int GetHashCode()
{
return UserId;
}
}
class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
Group g2 = obj as Group;
if (g2 == null) return false;
return GroupId == g2.GroupId;
}
public override int GetHashCode()
{
return GroupId;
}
}
You can do it with the basic structures like loops:
void Main()
{
var result = new string[] {"1","John Doe","2","Joes Group","3","Jack Daniel","4","Jacks Group","5","Foo Bar","6","FooBar Group",};
List<Person> personList = new List<Person>();
List<Group> groupList = new List<Group>();
for(int i = 0; i < result.Length; i+=2)
{
i = i + 2;
//check if group does not already exist
groupList.Add(new Group() {Name = result[i+1]});
}
for(int i = 0; i < result.Length; i+=2)
{
//check if person exists.
//if person only add group to person personList.Where(x => x.Name ==result[i+1])....
personList.Add(new Person() { Id = int.Parse(result[i]),
Name = result[i+1],
Groups = new List<Group>()
{
groupList.FirstOrDefault (l => l.Name == result[i+3])
}
});
i = i+2;
}
personList.Dump();
}
public class Person
{
public Person()
{
Groups = new List<Group>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<Group> Groups { get; set; }
}
public class Group
{
public string Name { get; set; }
}
// Define other methods and classes here
Output:
Please take advise: this code does not contain any validation logic, or duplicate checks. You'll have to imlpement this by yourself.
But before you implement something like this, I'd rather change the way you get your data delivered. this way you would deal with the root of your peroblems not with the symptoms.
i think no need to linq
//some class
public class Result
{
public string UserId {get;set;}
public string UserName {get;set;}
public string GroupId {get;set;}
public string GroupName {get;set;}
public string UserGroupUserId {get;set;}
public string UserGroupUserName {get;set;}
public string UserGroupId {get;set;}
public string UserGroupGroupId {get;set;}
}
// your array
private void Form1_Load(object sender, EventArgs e)
{
string[] result = new string[8];
result[0] = "1";
result[1] = "John Doe";
result[2] = "121";
result[3] = "SomeGroup";
result[4] = "1";
result[5] = "John Doe";
result[6] = "2135";
result[7] = "SomeOtherGroup";
Result r = CastResult(result);
}
// simple cast array to some type
public Result CastResult(string[] array)
{
return new Result() { UserId=array[0], UserName=array[1], GroupId=array[2], GroupName=array[3], UserGroupUserId=array[4], UserGroupUserName=array[5] , UserGroupId=array[6], UserGroupGroupId=array[7] };
}
I have two objects of same type with different values:
public class Itemi
{
public Itemi()
{
}
public int Prop1Min { get; set; }
public int Prop1Max { get; set; }
public int Prop2Min { get; set; }
public int Prop2Max { get; set; }
public int Prop3Min { get; set; }
public int Prop3Max { get; set; }
...................................
public int Prop25Min { get; set; }
public int Prop25Max { get; set; }
}
Now I instantiate two objects of this type and add some values to their properties.
Itemi myItem1 = new Itemi();
myItem1.Prop1Min = 1;
myItem1.Prop1Max = 4;
myItem1.Prop2Min = 2;
myItem1.Prop2Max = 4;
myItem1.Prop3Min = -1;
myItem1.Prop3Max = 5;
.............................
myItem1.Prop25Min = 1;
myItem1.Prop25Max = 5;
Itemi myItem2 = new Itemi();
myItem2.Prop1Min = 1;
myItem2.Prop1Max = 5;
myItem2.Prop2Min = -10;
myItem2.Prop2Max = 3;
myItem2.Prop3Min = 0;
myItem2.Prop3Max = 2;
................................
myItem2.Prop25Min = 3;
myItem2.Prop25Max = 6;
What is the best and fastest way to do this comparison:
take each properties from myItem1 and check if values from Prop1-25 Min and Max are within the range values of myItem2 Prop1-25 Min and Max
Example:
myItem1.Prop1Min = 1
myItem1.Prop1Max = 4
myItem2.Prop1Min = 1
myItem2.Prop1Max = 5
this is True because mtItem1 Prop1 min and max are within the range of myItem2 min and max.
the condition should be AND in between all properties so in the end after we check all 25 properties if all of them are within the range of the second object we return true.
Is there a fast way to do this using Linq or other algorithm except the traditional if-else?
I would refactor the properties to be more along the lines of:
public class Item
{
public List<Range> Ranges { get; set; }
}
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
}
Then your comparison method could be:
if (myItem1.Ranges.Count != myItem2.Ranges.Count)
{
return false;
}
for (int i = 0; i < myItem1.Ranges.Count; i++)
{
if (myItem1.Ranges[i].Min < myItem2.Ranges[i].Min ||
myItem1.Ranges[i].Max > myItem2.Ranges[i].Max)
{
return false;
}
}
return true;
Otherwise you will have to use Reflection, which is anything but fast.
Linq is using standart statements like if...then, for each and other, there is no magic :)
If the final goal only to compare, without needing to say, which properties are not in the range, then you not need to check them all, on the first unequals you can end checking.
Because you have so much properties, you must think about saving it in Dictionary, or List, for example. Or to use dynamic properties (ITypedList), if it will use for binding.
You really should do something like Ginosaji proposed.
But if you want to go with your current data model, here is how I would solve it. Happy typing. :)
public static bool RangeIsContained(int outerMin, int outerMax, int innerMin, int innerMax)
{
return (outerMin <= innerMin && outerMax >= innerMax);
}
public bool IsContained(Itemi outer, Itemi inner)
{
return RangeIsContained(outer.Prop1Min, outer.Prop1Max, inner.Prop1Min, inner.Prop1Max)
&& RangeIsContained(outer.Prop2Min, outer.Prop2Max, inner.Prop2Min, inner.Prop2Max)
// ...
&& RangeIsContained(outer.Prop25Min, outer.Prop25Max, inner.Prop25Min, inner.Prop25Max);
}
With your data model this is basically the only way to go except for reflection (slow!). LINQ cannot help you because your data is not enumerable.
For the sake of completeness, here is a LINQ solution (but it's less performant and less readable than Ginosaji's solution!)
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
public static bool IsContained(Range super, Range sub)
{
return super.Min <= sub.Min
&& super.Max >= sub.Max;
}
}
public class Itemi
{
public Itemi()
{
properties = new Range[25];
for (int i = 0; i < properties.Length; i++)
{
properties[i] = new Range();
}
}
private Range[] properties;
public IEnumerable<Range> Properties { get { return properties; } }
public static bool IsContained(Itemi super, Itemi sub)
{
return super.properties
.Zip(sub.properties, (first, second) => Tuple.Create(first, second))
.All((entry) => Range.IsContained(entry.Item1, entry.Item2));
}
public Range Prop1
{
get { return properties[0]; }
set { properties[0] = value; }
}
public Range Prop2
{
get { return properties[1]; }
set { properties[1] = value; }
}
// ...
}