I need sort list of nodes with fields: distance and frequency. I need that node with min distance and max frequency have been placed at the top.
list.OrderByDedcending(frequency).ThenBy(distance) - not that case.
I want get average between OrderBy(distance) and orderByDescending(frequency)
Example:
№ Distance frequency
1 6 15
2 4 10
3 5 3
I don't know how explain more clearly
I guess you want to order by weight,just like
class Demo
{
public int Distance { get; set; }
public int Frequency { get; set; }
public override string ToString()
{
return string.Format("Distance:{0} Frequency:{1}", this.Distance, this.Frequency);
}
}
List<Demo> list = new List<Demo>
{
new Demo{ Distance=3, Frequency=15},
new Demo{ Distance=4, Frequency=17},
new Demo{ Distance=5, Frequency=3},
};
int[] weight = { 30, 70 };
var tmp = list.OrderByDescending(x => x.Distance * 0.3 + x.Frequency * 0.7);//there just a guess
foreach(var q in tmp)
{
Console.WriteLine(q);
}
You can use the List.sort() with a comparer that you create to fit your needs :)
public class ObjectBasedComparer : IComparer<ObjectType>
{
public int Compare(ObjectType a, ObjectType b)
{
//Your logic here...
}
}
Related
I'll start by giving an example cause it will be easier.
Lets say I have a ListBox that's named "lbDestinations" and I have a class for that listbox called Destination.
Destination.cs
public class Destination
{
public string Name { get; set; }
public int Distance { get; set; }
public int Price { get; set; }
public Destination(string name, int distance, int price)
{
Name = name;
Distance = distance;
Price = price;
}
public override string ToString()
{
return string.Format("{0} {1} km {2} $", Name, Distance, Price);
}
}
Now I want to add destinations with a name, the distance to that location and the price for that trip. Lets have a couple destinations added for examples and stuff..
London 100 km 200 $
Berlin 400 km 800 $
Paris 200 km 400 $
Madrid 150 km 300 $
Now if I have all those in that order in the ListBox and I do lbDestinations.Sorted it will Sort them alphabetically, but I don't want that..
I want to sort them by the distance or the price.. The output has to be "Name Distance Price"
I tried a couple things but none of them worked
Instead of adding items to the listbox one by one, you can assign a collection to the DataSource of the listbox. This enables you to apply a custom sort to the collection.
E.g.
var destinations = new List<Destination>();
destinations.Add(new Desination { Name = "London", Distance = 100, Price = 200 });
...
destinations.Sort( (a, b) => a.Price.CompareTo(b.Price) );
lbDestinations.DataSource = null; // Ensures the listbox refreshes.
lbDestinations.DataSource = destinations;
You can also use LINQ to sort by more than one column:
var destinations = source
.OrderBy(d => d.Price)
.ThenBy(d => d.Name)
.ToList();
The accepted answer is excellent, but I'd be remiss if I didn't mention how DataGridView might save you a lot of legwork by automatically configuring itself based on your Destination class.
I have a class [...] called Destination. The output has to be "Name Distance Price".
public class Destination
{
[ReadOnly(true)]
public string Name { get; set; } = string.Empty;
[ReadOnly(true)]
public int Distance { get; set; }
[ReadOnly(false)]
public decimal Price { get; set; }
public override string ToString() =>
string.Format($"{Name} {Distance} km {Price} $");
}
I want to sort them by the distance or the price.
Implementation where consecutive clicks on same header alternates low-to-high or high-to-low.
private void sortByHeader(object? sender, DataGridViewCellMouseEventArgs e)
{
Destination[] tmp;
if (!e.ColumnIndex.Equals(-1))
{
var column = dataGridView.Columns[e.ColumnIndex].Name;
if (column.Equals(_prevColumn))
{
_highToLow = !_highToLow;
}
else
{
_highToLow = false;
_prevColumn = column;
}
switch (column)
{
case nameof(Destination.Name):
tmp = _highToLow ?
Destinations.OrderByDescending(_ => _.Name).ToArray() :
Destinations.OrderBy(_ => _.Name).ToArray();
break;
case nameof(Destination.Distance):
tmp = _highToLow ?
Destinations.OrderByDescending(_ => _.Distance).ToArray() :
Destinations.OrderBy(_ => _.Distance).ToArray();
break;
case nameof(Destination.Price):
tmp = _highToLow ?
Destinations.OrderByDescending(_ => _.Price).ToArray() :
Destinations.OrderBy(_ => _.Price).ToArray();
break;
default:
return;
}
Destinations.Clear();
foreach (var destination in tmp)
{
Destinations.Add(destination);
}
}
}
string? _prevColumn = null;
bool _highToLow = false;
Auto-Generate Columns
The method that loads the main form configures the DataGridView using the Destination class as a template and attaches the dataGridView.ColumnHeaderMouseClick event to sort the data. The DataSource of the data grid is set to Destinations which is a BindingList<Destination>.
public partial class MainForm : Form
{
public MainForm() =>InitializeComponent();
private readonly BindingList<Destination> Destinations= new BindingList<Destination>();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
dataGridView.RowTemplate.Height = 60;
// Add destinations interactively?
dataGridView.AllowUserToAddRows= false;
dataGridView.DataSource= Destinations;
#region F O R M A T C O L U M N S
Destinations.Add(new Destination()); // <= Auto-generate columns
dataGridView.Columns[nameof(Destination.Name)].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView.Columns[nameof(Destination.Distance)].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
dataGridView.Columns[nameof(Destination.Price)].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
dataGridView.Columns[nameof(Destination.Price)].DefaultCellStyle.Format = "F2";
Destinations.Clear();
#endregion F O R M A T C O L U M N S
dataGridView.ColumnHeaderMouseClick += sortByHeader;
addDestinationsToExample();
}
.
.
.
}
Now [...] add destinations with a name, the distance to that location and the price...
private void addDestinationsToExample()
{
Destinations.Add(new Destination
{
// * Had to make the Price sort different from Distance sort!
Name = "London - VIP",
Distance= 100,
Price= 1200,
});
Destinations.Add(new Destination
{
Name = "Berlin",
Distance= 400,
Price= 800,
});
Destinations.Add(new Destination
{
Name = "Paris",
Distance= 200,
Price= 400,
});
Destinations.Add(new Destination
{
Name = "Madrid",
Distance= 150,
Price= 300,
});
}
I hope this adds to your toolbox of possible ways to achieve your objective.
I am currently working on a project which uses two methods, one method returns the most accurate player list on a server with the duration the player was on the server, and the second method utilizes a different method which returns a player list with less accuracy and no time, but with an additional value which I need that other method doesn't have. To put it in simple terms:
Method 1:
List<PlayerObjectMethod1> playerListMethod1 = GetPlayersFromServerMethod1(serverIp, serverPort);
The class method 1:
public string Name { get; set; }
public float Duration { get; set; }
Method 2:
List<PlayerObjectMethod2> playersFromMethod2 = new List<PlayerObjectMethod2>();
The class method 1:
public string Name { get; set; }
public string SpecialValue { get; set; }
public string CustomDuration { get; set; }
Now as you can see the method 2 doesn't officially return duration, however this method is running every 15 seconds, so in theory, I could attach 15 seconds to each player every time it runs.
More background:
The parent method runs on a timer every 15seconds. There are 5 servers in total for one server (time in between specific server gets scanned) is around 18 seconds, as such each player on each call can be 18 seconds. I need to get an accurate player for that specific value. Two comparisons I want to do:
If a players name is not 123, only compare the name to get a specific value.
if(playerListMethod1[i].Name != "123") {
var index = playersFromMethod2.FindIndex(x => x==playerListMethod1[i].Name)
playersFromMethod2[index].IsOnline = True;
playersFromMethod2[index].Duration = playerListMethod1[i].Duration;
}
And now if it is 123 I need to find it by name and duration. However, the issue I have is how to upkeep that second list and add 15 seconds to all the players with name 123. As before I would use a list to store old player list value and just clear it and AddRange of the new one.
Example:
serverNotfPlayerListOld[server.Name].Clear();
serverNotfPlayerListOld[server.Name].AddRange(playersFromMethod2);
So I basically need an idea on how to do this, would I first fill the method2 with players, then check non 123 players, then check 123 players, and then add 15 seconds to the 123 players and at some point the list would get accurate?
Edit:
As mentioned before there are two different methods (two different sources) one gives name and duration, the other name and player id. As such, I need to somehow merge that data together. To do that I thought I could add my own duration for the second method because it's being run every 45 seconds. The current new code I have:
Example of the addition solution
class Program
{
static void Main()
{
HashSet<A> a = new HashSet<A>()
{
// add random values
new A { Id = "josh", Value = 60, },
new A { Id = "tom", Value = 60, },
new A { Id = "koven", Value = 120, },
new A { Id = "123", Value = 240, },
};
HashSet<A> b = new HashSet<A>()
{
// add random values (some with Id's from a)
new A { Id = "tom", Value = 10, },
new A { Id = "4123", Value = 10, },
new A { Id = "koven", Value = 65, },
new A { Id = "5552", Value = 60, },
new A { Id = "123", Value = 45, },
};
IEnumerable<A> c = IdJoin(a, b);
int i = 0;
foreach (A element in c)
{
Console.WriteLine($"{element.Id}: {element.Value}");
i++;
}
Console.WriteLine($"Count: {i}");
Console.WriteLine("Press [enter] to continue...");
Console.ReadLine();
}
public static IEnumerable<A> IdJoin(IEnumerable<A> a, IEnumerable<A> b)
{
Dictionary<string, A> dictionary = a.ToDictionary(i => i.Id);
foreach (A element in b)
{
if (dictionary.TryGetValue(element.Id, out A sameId))
{
if (element.Id == "123")
{
sameId.Value += element.Value;
}
else
{
sameId.Value += 45;
}
}
else {
dictionary.Add(element.Id, element);
}
}
return dictionary.Values;
}
}
public class A
{
public string Id;
public float Value;
}
Issue with this is that if it reads by only name it will bug out as multiple players can have 123. Which is why I need comparison method which gets by name and duration (of few minutes differences) in those two lists and I need help with that. Another example:
Two 123 players join the game. One list has values [name:123, duration:240],[name:123, duration:60] the other has [name:123, player:7548, customDuration: 225], [name:123, player:7555, customDuration: 90]
I need to get which player is which.
Presuming Id and Value combination makes unique value:
class Program
{
static List<A> firstList;
static List<A> secondList;
static List<A> resultList;
static void Main(string[] args)
{
// Fill firstList, secondList with data <your server methodes>
resultList = new List<A>();
foreach (var item in firstList)
{
var match = secondList.Find(a => a.Equals(item));
if (match != null)
{
if (item.Id == "123")
{
item.Value += match.Value;
}
else
{
item.Value += 45;
}
}
resultList.Add(item);
}
resultList.AddRange(secondList.Except(firstList));
}
}
public class A
{
public string Id;
public float Value;
public override bool Equals(Object obj)
{
if ((obj == null) || !GetType().Equals(obj.GetType()))
{
return false;
}
else
{
var a = (A)obj;
return (Id == a.Id) && (Value == a.Value);
}
}
}
Having a model something like this (I cannot change this):
public class SomeObject
{
public int Amount { get; set; }
public int TotalAmount { get; set; }
}
I need to iterate an array of SomeObject to populate some values and accumulate (perform not simple calculations) another fields.
static void Main(string[] args)
{
List<SomeObject> myCollection = new List<SomeObject>()
{
new SomeObject() { Amount = 3 },
new SomeObject() { Amount = 6 },
new SomeObject() { Amount = 9 }
};
int totalAccumulated = 0;
for (int i = 0; i < myCollection.Count; i++)
{
PopulateAndCalculate(myCollection[i], ref totalAccumulated);
}
//I don't want to create here a second for to iterate again all myCollection to set his TotalAmount property.
//There is another way?
Console.WriteLine($"The total accumulated is: {totalAccumulated}");
}
private static void PopulateAndCalculate(SomeObject prmObject, ref int accumulatedTotal)
{
//Populate a lot of another fields
accumulatedTotal += prmObject.Amount;
prmObject.TotalAmount = accumulatedTotal; //This don't work, but I need something alike
}
I don't want a second for statement to update TotalAmount property of each item in myCollection.
The main requirement is iterate the whole array, few times, don't care about string interpolation this is a short demo, this code must run in .net 2.0.
Theres is a clean/better way?
The solution is actually simple, though it's not exactly a good coding practice.
What you really need is for TotalAmount to be a static property. Without that, there's this:
static void Main(string[] args)
{
List<SomeObject> myCollection = new List<SomeObject>()
{
new SomeObject() { Amount = 3 },
new SomeObject() { Amount = 6 },
new SomeObject() { Amount = 9 }
};
int totalAccumulated = 0;
for (int i = 0; i < myCollection.Count; i++)
{
PopulateAndCalculate(myCollection[i], ref totalAccumulated);
}
/*****This is the new part*******/
myCollection[0].TotalAmount = totalAccumulated;
myCollection[1].TotalAmount = totalAccumulated;
myCollection[2].TotalAmount = totalAccumulated;
Console.WriteLine($"The total accumulated is: {totalAccumulated}");
}
private static void PopulateAndCalculate(SomeObject prmObject, ref int accumulatedTotal)
{
//Populate a lot of another fields
accumulatedTotal += prmObject.Amount;
//no need to mess with the total here as far as the properties are concerned.
}
You can st fields inside linq expression.
Could you consider this please
myCollection.ForEach(c => c.TotalAmount = myCollection.Sum(a => a.Amount));
Console.WriteLine($"Total accumulated :{myCollection.First().TotalAmount}");
I found a solution using the Observer Pattern.
Firstly I created a global delegate to be used by an event:
public delegate void UpdateTotalAmountDelegate(int totalAmount);
Then a new class called: 'CalculatorSetter'
public class CalculatorSetter
{
public event UpdateTotalAmountDelegate UpdateTotalAmounthHandler;
public void UpdateTotalAmount(int prmTotalAmount)
{
UpdateTotalAmounthHandler(prmTotalAmount);
}
}
I refactor the data object 'SomeObject' adding a field of type CalculatorSetter.
public class SomeObject
{
private CalculatorSetter finalCalculator;
public void SetCalculator(CalculatorSetter prmCalculator)
{
this.finalCalculator = prmCalculator;
finalCalculator.UpdateTotalAmounthHandler += FinalCalculator_UpdateTotalAmounthHandler;
}
private void FinalCalculator_UpdateTotalAmounthHandler(int totalAmount)
{
this.TotalAmount = totalAmount;
}
//Some Other Fields
public int Amount { get; set; }
public int TotalAmount { get; set; }
}
And my original code and unique for:
static void Main(string[] args)
{
List<SomeObject> myCollection = new List<SomeObject>()
{
new SomeObject() { Amount = 3 },
new SomeObject() { Amount = 6 },
new SomeObject() { Amount = 9 }
};
CalculatorSetter commonCalculator = new CalculatorSetter();
int totalToAccumulate = 0;
for (int i = 0; i < myCollection.Count; i++)
{
PopulateAndCalculate(myCollection[i], commonCalculator, ref totalToAccumulate);
}
commonCalculator.UpdateTotalAmount(totalToAccumulate);
Console.WriteLine($"The total accumulated is: {totalToAccumulate}");
Console.WriteLine($"The first total accumulated is: {myCollection[0].TotalAmount}");
}
Many thanks.
Use a wrapper and keep it simple (if you want you can change a little for use static methods you can, or static class but I dont see the point)
the result is:
The Amount is 3, The total ammount is 18
The Amount is 6, The total ammount is 18
The Amount is 9, The total ammount is 18
namespace Prueba1
{
class Program
{
public class WrapperInt {
public int Value { get; set; }
}
public class SomeObject
{
public int Amount { get; set; }
public WrapperInt TotalAmount { get; set; }
}
public Program() {
WrapperInt TotalAmountAllArrays = new WrapperInt();
List<SomeObject> myCollection = new List<SomeObject>()
{
new SomeObject() { Amount = 3, TotalAmount =TotalAmountAllArrays },
new SomeObject() { Amount = 6 , TotalAmount =TotalAmountAllArrays },
new SomeObject() { Amount = 9 , TotalAmount =TotalAmountAllArrays }
};
for (int i = 0; i < myCollection.Count; i++)
{
myCollection[i].TotalAmount.Value += myCollection[i].Amount;
}
foreach (var c in myCollection)
{
Console.WriteLine($"The Amount is:" + c.Amount + " The total ammount is:" + c.TotalAmount.Value);
}
}
static void Main(string[] args)
{
new Program();
}
}
}
Hopefully this will work for you… One possible solution is to create a wrapper class called MyTotalList which contains a List named amounts and an int named total. MyTotalList class does not expose its list amounts as an editable list. If the class exposes this list as editable, then other methods could ultimately change an items value in that list and the MyTotalList class would not be aware of this and unfortunately contain an incorrect total. To avoid this situation and for the class to work as expected, methods must use the MyTotalList’s Add and Remove methods. To ensure this happens, the private List amounts in the MyTotalList class returns a read only list which ensures that changes to the list will not be made outside the MyTotalList class. Leaving the list exposed and editable will/could cause the class to contain an incorrect total.
My solution is to create a Class that wraps a List. MyTotalList class has a no argument constructor. Once a new instance of a MyTotalList object is created you can then use that instance to Add MyObject items to its list. Every time an item is added to the MyTotalList, list amounts the variable total gets updated with the added item’s amount. Example:
Create a new MyTotalList object:
MyTotalList listOfObjects = new MyTotalList();
Then add some MyObject instances to the listOfObjects
listOfObjects.Add(new MyObject(1,3));
listOfObjects.Add(new MyObject(2,6));
listOfObjects.Add(new MyObject(3,9));
After you add the items, you can then use the listOfObjects Total property to get the total sum of all MyObject items in the list with:
listOfObjects.Total
If you need to pass or use the List of MyTotalList items you can use:
listOfObjects.Items
Bear in mind as I discussed above, this List Items is a read-only list. Therefore you cannot add/remove items in this list as you would an editable list. So the code below will fail during implementation as these methods are not exposed for read only objects.
listOfObjects.Items.Remove(new MyObject(4, 10));
listOfObjects.Items.Add(new MyObject(4, 10));
The above lines will cause the compiler to complain: xxx… does not contain a definition for Add/Remove. This ensures methods will use the MyTotalList.Add and MyTotalsList.Remove methods and eliminate any possibility of the list changing outside the MyTotalList class.
MyObject Class
class MyObject : IComparable {
public int id { get; }
public int amount { get; }
public MyObject(int inID, int inAmount) {
id = inID;
amount = inAmount;
}
public override string ToString() {
return amount.ToString();
}
public override int GetHashCode() {
return id.GetHashCode();
}
public override bool Equals(object other) {
if (other != null)
return (this.id == ((MyObject)other).id);
return false;
}
public int CompareTo(object other) {
if (this.id > ((MyObject)other).id)
return 1;
if (this.id < ((MyObject)other).id)
return -1;
return 0;
}
}
MyTotalList Class
class MyTotalList {
private int total;
private List<MyObject> amounts;
public MyTotalList() {
total = 0;
amounts = new List<MyObject>();
}
public int ListCount {
get { return amounts.Count; }
}
public IReadOnlyCollection<MyObject> Items {
get { return amounts.AsReadOnly(); }
}
public int Total {
get { return total; }
}
public void Add(MyObject other) {
if (other != null) {
if (!(amounts.Contains(other))) {
total += other.amount;
amounts.Add(other);
}
else {
Console.WriteLine("Duplicate id's not allowed!");
}
}
}
public void Remove(MyObject other) {
if (amounts.Contains(other)) {
total -= amounts[amounts.IndexOf(other)].amount;
amounts.Remove(other);
}
else {
Console.WriteLine("Item to remove not found!");
}
}
}
Examples
MyTotalList listOfObjects = new MyTotalList();
listOfObjects.Add(new MyObject(1,3));
listOfObjects.Add(new MyObject(2,6));
listOfObjects.Add(new MyObject(3,9));
Console.WriteLine("----------------------------------------");
Console.WriteLine("Initial list with total");
Console.WriteLine("items in list:");
foreach (MyObject mo in listOfObjects.Items)
Console.Write(mo.ToString() + " ");
Console.WriteLine();
Console.WriteLine("Total from list of " + listOfObjects.ListCount +
" items is: " + listOfObjects.Total);
Console.WriteLine("----------------------------------------");
Console.WriteLine("Add three more items");
listOfObjects.Add(new MyObject(4, 10));
listOfObjects.Add(new MyObject(5, 11));
listOfObjects.Add(new MyObject(6, 12));
Console.WriteLine("items in list:");
foreach (MyObject mo in listOfObjects.Items)
Console.Write(mo.ToString() + " ");
Console.WriteLine();
Console.WriteLine("Total from list of " + listOfObjects.ListCount +
" items is: " + listOfObjects.Total);
Console.WriteLine("----------------------------------------");
Console.WriteLine("Remove id 4 (10) from the list");
listOfObjects.Remove(new MyObject(4, 10));
Console.WriteLine("items in list:");
foreach (MyObject mo in listOfObjects.Items)
Console.Write(mo.ToString() + " ");
Console.WriteLine();
Console.WriteLine("Total from list of " + listOfObjects.ListCount +
" items is: " + listOfObjects.Total);
A Side note to your original post…About the class you can not change
SomeObject {
public int Amount { get; set; }
public int TotalAmount { get; set; }
}
Regardless of how you get the total for theint varable: TotaAmount… for each instance of SomeObject class to contain the same variable with the same amount and you want to ensure this is true for all existing SomeObject instances… is well a poor design. This creates redundant data and simply waste space and it makes no sense for each variable to contain this value as it has absolutely nothing to do with that SomeObject instance. This class design is counter intuitive of a good design. As #Tim Schmelter’s comment points out "a single object should not know anything about the total amount of other objects." This “redundant data” situation is something a programmer should try to avoid, not promote.
I have an array of items with two properties, name and position. The array is not sorted in any way and I want to populate a listbox in order of position.
I can do it with this code down below, first I add all items so I have a list with correct numbers of items and then replace the items at correct position. But I wonder if there is a better way to do it?
I want the listbox to have the names in this order: Mark, John and James.
Note: the James, Mark and John data is just an example, and I can't sort the standing array.
public class _standing
{
public _standing(string _name, int _pos) {
name = _name;
position = _pos;
}
public string name { get; set; }
public int position { get; set; }
}
_standing a = new _standing("James", 2);
_standing b = new _standing("Mark", 0);
_standing c = new _standing("John", 1);
_standing[] standing = new _standing[]{a, b, c};
for (int i = 0; i < standing.Length; i++) {
listBox1.Items.Add(standing[i].name);
}
for (int i = 0; i < standing.Length; i++) {
listBox1.Items.RemoveAt(standing[i].position);
listBox1.Items.Insert(standing[i].position, standing[i].name);
}
You can just use the OrderBy method of the array:
standing = standing.OrderBy(i => i.position).ToArray();
listBox1.Items.AddRange(standing);
You can also order by decscending:
standing.OrderByDescending(i => i.position).ToArray();
These both require a reference to System.Linq
Also, since OrderBy returns a new object, you can also do this without re-ordering your original list:
_standing a = new _standing("James", 2);
_standing b = new _standing("Mark", 0);
_standing c = new _standing("John", 1);
_standing[] standing = new _standing[] { a, b, c };
listBox1.Items.AddRange(standing.OrderBy(i => i.position).ToArray());
Update
In order to show something meaningful in your listBox1, you should override the ToString method on your _standing class, something like:
public class _standing
{
public string name { get; set; }
public int position { get; set; }
public _standing(string _name, int _pos)
{
name = _name;
position = _pos;
}
public override string ToString()
{
return position + ": " + name;
}
}
Finally, I have to mention that your casing/naming conventions are not standard C#. The standard is for Classes and Properties to be PascalCase, for arguments to be camelCase, and for private fields to be pascalCase with an optional underscore prefix. So your code would ideally look something like:
public class Standing
{
public string Name { get; set; }
public int Position { get; set; }
public Standing(string name, int position)
{
Name = name;
Position = position;
}
public override string ToString()
{
return Position + ": " + Name;
}
}
and...
Standing a = new Standing("James", 2);
Standing b = new Standing("Mark", 0);
Standing c = new Standing("John", 1);
Standing[] standing = { a, b, c };
listBox1.Items.AddRange(standing.OrderBy(i => i.Position).ToArray());
If I were you I would create a list of _standing first and add to the list. Something like:
List<_standing> standingList = new List<_standing>();
standingList.Add(new _standing("James", 2));
etc...
The advantage of using a List over the array type is that it's size is dynamic. If you are only ever going to have 3 _standing objects then an array is fine, but in reality that is probably unlikely to be the case.
Then you can use a Linq query to sort the list by position
IEnumerable<_standing> sorted = standingList.OrderBy(stand => stand.Position);
You now have a sorted List using .NET built in sorting algorithms and you can add this to your control Items collection. You can save time by using the AddRange method:
listBox1.Items.AddRange(sorted);
Sources for reference:
Sorting a collection
Add range
I am researching the best way to store a structure and have it easily searchable for a single value returning the key. Here is the pseduo data structure:
N = 0
NNE = 1 .. 44
NE = 45
ENE = 46 .. 89
E = 90
ESE = 91 .. 134
SE = 135
SSE = 136 .. 179
S = 180
SSW = 181 .. 224
SW = 225
WSW = 226 .. 269
W = 270
WNW = 271 .. 314
NW = 315
NNW = 316 .. 359
I would like to be able to store these values in a way that I can say something like:
Give me the key value for a given value. So if I need the key for 193, I would be returned SSW. I have been playing around with different ideas, but want to see what you guys think.
I am using wind direction as the example, but the data could be whatever.
The data structure will be compiled and never changes.
Thanks.
You could create a class to hold a "key" (I think "name" is a more appropriate descriptor, but call it what you wish) and range of values on the compass, e.g.:
public class CompassRange
{
public string Name { get; set; }
public int Min { get; set; }
public int Max { get; set; }
}
Then, create class which creates a static List<CompassRange> and populate it appropriately:
public class Compass
{
private static List<CompassRange> _ranges;
static Compass()
{
_ranges = new List<CompassRange>()
{
// Add CompassRange objects here
};
}
}
Finally, you can add a method to this class that will search the List for the appropriate range and return the name:
public static string GetName(int direction)
{
direction = direction % 360;
return _ranges.First(x => x.Min <= direction && x.Max >= direction).Name;
}
You could even use the built-in System.Tuple<string, int, int> type instead of CompassRange, although this sacrifices some of the clarity of this code.
If you store the Min, Max, and Direction in a class, you could easily just populate a list of these, and find a direction with a single LINQ query:
// Given:
class Direction
{
public Direction(string dir, int min, int max)
{
MinHeading = min;
MaxHeading = max;
Direction = dir;
}
public int MinHeading { get; private set; }
public int MaxHeading { get; private set; }
public string Direction { get; private set; }
}
// And a collection:
var directions = new List<Direction>
{
new Direction("N",0,0),
new Direction("NNE",1,44),
...
}
// You can find a direction given
int compassHeading = 93;
string direction = directions
.First(d => compassHeading >= d.MinHeading && compassHeading <= d.MaxHeading)
.Select(d => d.Direction);