I cannot solve a problem for several hours now.
Here is a simplified scenario.
Let's say there is a list of people with their bids. I'm trying to find a person with the highest bid and return the name. I am able to find the highest bid, but how to I output the name?
List<String[]> list = new List<String[]>();
String[] Bob = { "Alice", "19.15" };
String[] Alice = {"Bob", "28.20"};
String[] Michael = { "Michael", "25.12" };
list.Add(Bob);
list.Add(Alice);
list.Add(Michael);
String result = list.Max(s => Double.Parse(s.ElementAt(1))).ToString();
System.Console.WriteLine(result);
As a result I get 28.20, which is correct, but I need to display "Bob" instead. There were so many combinations with list.Select(), but no success. Anyone please?
The best solution from an architectural point of view is to create a separate class (e.g. Person) that contains two properties Name and Bid of each person and a class Persons that contains the list of persons.
Then you can easily use a LINQ command.
Also instead of storing bids as string, think if bids as floating point or decimal values would be better (or store it in cents and use an int).
I don't have a compiler by hand so it's a bit out of my head:
public class Person
{
public string Name { get; set; }
public float Bid { get; set; }
public Person(string name, float bid)
{
Debug.AssertTrue(bid > 0.0);
Name = name;
Bid = bid;
}
}
public class Persons : List<Person>
{
public void Fill()
{
Add(new Person("Bob", 19.15));
Add(new Person("Alice" , 28.20));
Add(new Person("Michael", 25.12));
}
}
In your class:
var persons = new Persons();
persons.Fill();
var nameOfHighestBidder = persons.MaxBy(item => item.Bid).Name;
Console.WriteLine(nameOfHighestBidder);
This works in the simple example. Not sure about the real one
var result = list.OrderByDescending(s => Double.Parse(s.ElementAt(1))).First();
You can use Jon Skeet's MaxBy.
For usage you can see this question
e.g. in this case
list.MaxBy(s => Double.Parse(s.ElementAt(1)))[0]
More here
Should work:
var max = list.Max(t => double.Parse(t[1]));
list.First(s => double.Parse(s[1]) == max)[0]; // If list is not empty
After finding result just do as below:
list.First(x=>x[1] == result)[0]
Related
I was wondering if there's a simpler way to do this using LINQ or some other way...
I want to extract, from a List, all rows, grouping them by a particular field and storing the result in another different, like a List containing the grouped, not repeated, result of the search parameter.
Here's an example:
public class CustomObj
{
public int DOC { get; set; }
public string Desc { get; set; }
public string Name{ get; set; }
}
public class Worker()
{
public void DoWork()
{
List<CustomObj> objs = new List<CustomObj>();
objs.Add(new CustomObj(1, "Type1", "Name1"));
objs.Add(new CustomObj(1, "Type2", "Name1"));
objs.Add(new CustomObj(2, "Type2", "Name2"));
objs.Add(new CustomObj(3, "Type1", "Name1"));
objs.Add(new CustomObj(3, "Type2", "Name1"));
objs.Add(new CustomObj(3, "Type3", "Name1"));
// HERE'S WHAT I'D LIKE TO DO
// NOTE THAT THIS IS NOT A CORRECT FORMATED FUNCTION
List<int> docs = objs.GroupBy(o => o.DOC).Select(o => o.DOC).ToList<int>();
}
}
At the end my docs List should look like this
docs[0] = 1
docs[1] = 2
docs[2] = 3
In my current project I have, so far, 10 different objects, and I'm going to have to do this to all of them. Of course I'd like to avoid writing extensive functions to each one.
So if there's no other way at least I would like to know.
Thanks
What about:
List<int> docs = objs.Select(o => o.DOC).Distinct().ToList();
Or, if you want to use GroupBy:
var docs = objs.GroupBy(o => o.DOC).Select(o => o.Key).ToList();
I have DTO class that defines order line like this:
public class Line
{
public string Sku { get; set; }
public int Qty { get; set; }
}
A list of type Line is populated like so:
List<Line> myLines = new List<Line>();
myLines.Add(new Line() { Sku = "ABCD1", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD2", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD3", Qty = 1 });
What I want is to use LINQ to get an array of SKUs from the myLines List. How can I go about doing that?
I am currently doing it manually like this ...
// Get SKU List
List<string> mySKUs = new List<string>();
foreach (Line myLine in myLines)
mySKUs.Add(myLine.Sku);
string[] mySKUsArray = mySKUs.ToArray();
I was trying to google for a solution, but I wasn't sure how to word the question...
P.S. is there any benefit/performance gain in using LINQ method to achieve what I am currently doing with foreach?
You can use:
var mySKUs = myLines.Select(l => l.Sku).ToList();
The Select method, in this case, performs a mapping from IEnumerable<Line> to IEnumerable<string> (the SKU), then ToList() converts it to a List<string>.
Note that this requires using System.Linq; to be at the top of your .cs file.
This is very simple in LinQ... You can use the select statement to get an Enumerable of properties of the objects.
var mySkus = myLines.Select(x => x.Sku);
Or if you want it as an Array just do...
var mySkus = myLines.Select(x => x.Sku).ToArray();
I think you're looking for;
string[] skus = myLines.Select(x => x.Sku).ToArray();
However, if you're going to iterate over the sku's in subsequent code I recommend not using the ToArray() bit as it forces the queries execution prematurely and makes the applications performance worse. Instead you can just do;
var skus = myLines.Select(x => x.Sku); // produce IEnumerable<string>
foreach (string sku in skus) // forces execution of the query
You can select all Sku elements of your myLines list and then convert the result to an array.
string[] mySKUsArray = myLines.Select(x=>x.Sku).ToArray();
In the case you're interested in extremely minor, almost immeasurable performance increases, add a constructor to your Line class, giving you such:
public class Line
{
public Line(string sku, int qty)
{
this.Sku = sku;
this.Qty = qty;
}
public string Sku { get; set; }
public int Qty { get; set; }
}
Then create a specialized collection class based on List<Line> with one new method, Add:
public class LineList : List<Line>
{
public void Add(string sku, int qty)
{
this.Add(new Line(sku, qty));
}
}
Then the code which populates your list gets a bit less verbose by using a collection initializer:
LineList myLines = new LineList
{
{ "ABCD1", 1 },
{ "ABCD2", 1 },
{ "ABCD3", 1 }
};
And, of course, as the other answers state, it's trivial to extract the SKUs into a string array with LINQ:
string[] mySKUsArray = myLines.Select(myLine => myLine.Sku).ToArray();
I am taking a C# class and I have a project. The project has code written for void Main(). Here is the first part of the code in Main():
const string STUDENT_FILE = #"C:\TEMP\Students.txt";
const string ASSIGNMENT_FILE = #"C:\Temp\Assignments.txt";
Students students=FileRoutines.LoadStudents(STUDENT_FILE));
I have to write a 3 classes Students, student, and FileRoutines. I cannot seem to figure out what needs to go into the Students class to get the instance to work. I can write the following:
string[] students = FileRoutine.LoadStudents(STUDENT_FILE)
and get the students array I am trying to do with:
Students students=FileRoutines.LoadStudents(STUDENT_FILE));
but I cannot seem to get this instance to work. This is the contents of the file:
122338 Weltzer Teresa
123123 Wang Choo
123131 Adams James
123132 Binkley Joshua Troy
123139 Howard Tyler
123160 King Alma
I have seen this project posted before, but no one seems to be able to answer the question, but instead offer other alternatives.
I know when creating an instance of a class I need to have a constructor or constructors, but I am not doing:
Students students = new Students();
So far I am lost on what to do.
Thanks
Dave
To implement as you ask, FileRoutines.LoadStudents(STUDENT_FILE) has to return a Students instance, so you need to modify that method to construct the new Students and return that instead of string[]
So perhaps your Students class takes the string[] in its constructor?
That should be enough to get you going?
Obviously, the Students type has to behave like a collection of some kind. I propose this
A class for a student data is a piece of cake.
class Student
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now things get a little more interesting. The following class implements IEnumerable of Student in order to provide the most crucial methods for looping through its inner structure. Is quite easy once you get this idea. You already store students in some collection so just return its enumerator instead of creating a custom one.
class Students : IEnumerable<Student>
{
private readonly List<Student> students;
public Students(IEnumerable<Student> students)
{
this.students = students.ToList();
}
public IEnumerator<Student> GetEnumerator()
{
return students.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return students.GetEnumerator();
}
}
And finally, a class with a method for loading from a file. This can be done many ways, but I find LINQ a nice tool for data querying.
class FileRoutines
{
public static Students LoadStudents(string path)
{
var lines = File.ReadLines(path);
var students = lines.Select(l => l.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
.Select(split => new Student
{
Id = split[0],
LastName = split[1],
FirstName = split[2]
});
return new Students(students);
}
}
This question is purely academic for me and a spinoff of a question I answered here.
Retrieve object from an arraylist with a specific element value
This guy is using a plain ArrayList... I Know not the best thing to do ... filled with persons
class Person
{
public string Name { get; set; }
public string Gender { get; set; }
public Person(string name, string gender)
{
Name = name;
Gender = gender;
}
}
personArrayList = new ArrayList();
personArrayList.Add(new Person("Koen", "Male"));
personArrayList.Add(new Person("Sheafra", "Female"));
Now he wants to select all females. I solve this like this
var females = from Person P in personArrayList where P.Gender == "Female" select P;
Another guy proposes
var persons = personArrayList.AsQueryable();
var females = persons.Where(p => p.gender.Equals("Female"));
But that does not seem to work because the compiler can never find out the type of p.
Does anyone know what the correct format for my query would be in the second format?
You can use Cast<T> to cast it to a strongly typed enumerable:
var females = personArrayList.Cast<Person>()
.Where(p => p.gender.Equals("Female"));
Cast<T> throws exception if you have anything other than Person in your arraylist. You can use OfType<T> instead of Cast<T> to consider only those objects of type Person.
On a side note, kindly use an enum for gender, not strings.
enum Sex { Male, Female }
class Person
{
public Sex Gender { get; set; }
}
Since the ArrayList has untyped members, you'll have to cast the members to Person:
var females = persons.OfType<Person>().Where(p => p.gender.Equals("Female"));
Cast personArrayList to its element type and you are done.
var persons = personArrayList.Cast<Person>();
Probably this question was already asked before, but my google-fu and SO-Search did not get me what what I was looking for.
I have a custom class, and a custom class comparer (for checking the equality of the class) implemented with IEqualityComparer.
public class Person
{
public string Name { get; set; }
public bool Flag { get; set; }
}
public class PersonComparer : IEqualityComparer<Person>
{
#region IEqualityComparer<Person> Members
public bool Equals(Person x, Person y)
{
//case insensitive compare
return string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode(Person obj)
{
return base.GetHashCode();
}
#endregion
}
and in the main portion of the code I have 2 lists "source" and "target"
Person bob = new Person() { Name = "Bob" };
Person sam = new Person() { Name = "Sam" };
Person andy = new Person() { Name = "Andy" };
Person thomas = new Person() { Name = "Thomas" };
Person jimmy = new Person() { Name = "Jimmy" };
Person sam2 = new Person() { Name = "sam" }; // note the lower case
Person jane = new Person() { Name = "Jane" };
List<Person> source = new List<Person>() { bob, sam, andy, thomas };
List<Person> target = new List<Person>() { sam2, andy,jane };
what I want to do
update source list to only contain sam and andy, as bob and thomas are not in the target list. I did this
source = (from p in source where (from t in target select t)
.Contains(p, new PersonComparer())
select p).ToList();
In the target I should "Flag" sam2 and andy to true and jane is flagged as "false" by default, I should not change it.
I tried using this, but this removes "jane" from target
//sets sam2 & andy to true, removes Jane
target = (from p in target.Select(t => { t.Flag = true; return t; })
where (from s in source
select s).Intersect(select p).ToList();
Can any LINQ guru tell me what I am doing wrong ?
3.Is there a better way to write Query 1 ?
4.And finally a trivial question: how exactly do you say "=>" when you are talking to a fellow coder over the phone
As Sander has pointed out, LINQ is for querying, not updating.
However, to answer the questions... Your original query of
source = (from p in source where (from t in target select t)
.Contains(p, new PersonComparer()) select p).ToList();
would be much more simply written as:
source = source.Intersect(target, new PersonComparer()).ToList();
Having said that, you need to update PersonComparer as recursive mentioned. It should be something like this:
public int GetHashCode(Person obj)
{
return obj == null ? 0
: StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name);
}
I'm afraid I don't really understand your second query particularly well... but if you want to change the existing objects, I'd suggest a foreach loop instead of trying to use LINQ. Queries with side-effects are generally a bad idea.
You may mean something like:
// You may want to make some singleton instance available, as this has no state
PersonComparer comparer = new PersonComparer();
foreach (Person person in target)
{
if (source.Contains(person, comparer))
{
person.Flag = true;
}
}
Linq isn't meant to update list, because it operates on IEnumerable<T>. You can create a new enumerable, based on source and target that represents the collection you need.
Something like this should work:
var combined = source.Where(x => target.Any(y => y == x))
For part 4. => can be read as goes to.
The GetHashCode() method should use the the obj passed instance, not its own parent.