C# mapping compass points and searching - c#

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);

Related

cast child class to another with base class

I need to have some Length units that can convert them together
Length class as parent
Meter, Centimeter, Millimeter as Childs:
public abstract class Length
{
}
public class Meter: Length
{
public Meter(double val)
{
Value = val;
}
public double Value { get; set; }
public static explicit operator Centimeter(Meter m)
{
return new Centimeter(m.Value * 100);
}
public static explicit operator Millimeter(Meter m)
{
return new Millimeter(m.Value * 1000);
}
}
public class Centimeter: Length
{
public Centimeter(double val)
{
Value = val;
}
public double Value { get; set; }
public static explicit operator Meter(Centimeter cm)
{
return new Meter(cm.Value / 100);
}
public static explicit operator Millimeter(Centimeter cm)
{
return new Millimeter(cm.Value * 10);
}
}
public class Millimeter: Length
{
public Millimeter(double val)
{
Value = val;
}
public double Value { get; set; }
public static explicit operator Meter(Millimeter mm)
{
return new Meter(mm.Value / 1000);
}
public static explicit operator Centimeter(Millimeter mm)
{
return new Centimeter(mm.Value / 10);
}
}
I can cast Meter to Millimeter with this code:
Meter m = new Meter(3)
Millimeter mm = (Millimeter)m; //it's ok. result is 3000
But I need to have base class type to hold my variable:
Length l;
if (true)
l = new Meter(3);
else
l = new Centimeter(20)
Millimeter m = (Millimeter)l;//runtime Error
I get runtime error :
System.InvalidCastException: 'Unable to cast object of type 'Meter' to type 'Millimeter'.'
This might be in the direction you are hoping to get. Having one class be able to overall handle either conversion of millimeter, centimeter or meter. In this class sample, I am having the class store the value to the lowest granularity of millimeter. Now, that said, you might even want the millimeters level down to an integer vs a double, unless you want to allow for fraction of millimeter, but your choice.
public class MyLength
{
// always store at the lowest possible scale here
private double _millsValue;
private MyLength( double alwaysMillimeters)
{
_millsValue = alwaysMillimeters;
}
// have constructor return a MyLength based on whatever type wanted with
// the STATIC method name make sure it builds out to proper millimeter reference
public static MyLength Millimeter( double val)
{ return new MyLength(val); }
// 10 millimeters to 1 centimeter, so an incoming 5 centimeters = 50 millimeters
public static MyLength Centimeter( double val)
{ return new MyLength(val * 10); }
// similarly, 1000 millimeters to 1 meter, so 2 meters = 2000 millimeters
public static MyLength Meter (double val)
{ return new MyLength(val * 1000); }
// Now, expose public getter / setter that are bound to the
// underlying _millsValue and divide back out by 10 or 1000 respectively
public double Millimeters
{
get { return _millsValue; }
set { _millsValue = value; }
}
public double Centimeters
{
get { return _millsValue / 10; }
set { _millsValue = value * 10; }
}
public double Meters
{
get { return _millsValue / 1000; }
set { _millsValue = value * 1000; }
}
}
And to show how all applied and getting values out regardless of basis created and handling the conversion for you via getters / setters
public class LengthTest
{
public LengthTest()
{
var L = MyLength.Millimeter(1000);
var mm = L.Millimeters; // returns 1000
var cm = L.Centimeters; // returns 100
var m = L.Meters; // returns 1
var C = MyLength.Centimeter(3);
mm = C.Millimeters; // returns 30
cm = C.Centimeters; // returns 3
m = C.Meters; // returns .03
var M = MyLength.Meter(2);
mm = M.Millimeters; // returns 2000
cm = M.Centimeters; // returns 200
m = M.Meters; // returns 2
// can also SET values based on respective named getter/setter
var someVal = MyLength.Centimeter(0);
someVal.Centimeters = 250;
mm = someVal.Millimeters; // returns 2500
cm = someVal.Centimeters; // returns 250
m = someVal.Meters; // returns 2.5
someVal.Meters = .75;
mm = someVal.Millimeters; // returns 750
cm = someVal.Centimeters; // returns 75
m = someVal.Meters; // returns .75
someVal.Millimeters = 250;
mm = someVal.Millimeters; // returns 250
cm = someVal.Centimeters; // returns 25
m = someVal.Meters; // returns .25
}
}

Any suggestion how can I do this better?

First I would like to say sorry that I didn't use english but you will have the general idea of what I am trying to do.
Create class subjects variables and characteristics / properties:
- kodiILendes
- emriILendes
- nota
The method Main should make it possible for users to introduce these data, code
file, the file name and grade.
You need to create objects for 5 subjects
Make use of accessories (get and set), to mark So while you should assign grades should not be
less than 5 and also must not be greater than 10.
And to submit your average for this semester format, p.sh .: "Your Average
It is 9.3 ".
class Lendet
{
public int kodiIlendes;
public string emriIlendes;
private int nota;
public int Nota {
get {
return nota;
}
set {
if (value > 5 && value <= 10)
{
nota = value;
}
else {
Console.WriteLine("Nota duhet te jet me e > se 5 dhe nuk duhet te jet me e > se 10 ");
}
}
}
}
static void Main(string[] args)
{
Lendet Anglisht = new Lendet();
Anglisht.kodiIlendes = 100;
Anglisht.emriIlendes = "Anglisht";
Anglisht.Nota = 10;
}
Now lets imagine I created the 5 objects and I want to find the average.How can I do that ?
One way is like this Console.WriteLine(x.Nota+y.Nota+z.Nota+b.Nota+c.Nota/5)
class Lendet
{
public int kodiIlendes;
public string emriIlendes;
public static float sum;
public static int count;
public Lendet()
{
count++;
}
private int nota;
public int Nota {
get {
return nota;
}
set {
if (value > 5 && value <= 10)
{
sum =sum+value;
nota = value;
}
else {
Console.WriteLine("Nota duhet te jet me e > se 5 dhe nuk duhet te jet me e > se 10 ");
}
}
}
}
static void Main(string[] args)
{
//create object1
// create object2
//......create object n
Console.WriteLine(Lendet.sum/Lendet.count);
}
create two static variables, one for count of objects created and other for sum. Divide second by first to get the average.
My approach, with history:
class Lendet
{
public int Nota { get; private set; }
public Lendet(int nota)
{
this.Nota = nota;
LendetHistory.Add(this);
}
}
static class LendetHistory
{
private static List<Lendet> lendets = new List<Lendet>();
public static float Average()
{
if(lendets.Count < 1)
return 0;
return lendets.Select(s => s.Nota).Average();
}
public static void Add(Lendet lendet)
{
lendets.Add(lendet);
}
}
use in code:
var k = new Lendet(10);
var c = new Lendet(20);
Console.WriteLine(LendetHistory.Average());
and with that approach you can expand your logic

How to sort list by many criterias

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...
}
}

How to populate a listbox sorted with unsorted array

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

Arrays and measurement unit conversion

using (read = new StreamReader("C:/Users/Sam Smith/Desktop/convert.txt"))
{
while (!read.EndOfStream)
{
lineFromFile = read.ReadLine();
units = lineFromFile.Split(',');
if (units.Contains(splitEntry[0]) && units.Contains(splitEntry[1]))
{
firstUnit = units[0];
secondUnit = units[1];
userConvertValue = Convert.ToDouble(splitEntry[2]);
fileConvertValue = Convert.ToDouble(units[2]);
result = fileConvertValue * userConvertValue;
}
if (units.Contains(splitEntry[0]) && units.Contains(splitEntry[1]))
{
firstUnit = units[1];
secondUnit = units[0];
userConvertValue = Convert.ToDouble(splitEntry[2]);
fileConvertValue = Convert.ToDouble(units[2]);
result = userConvertValue / fileConvertValue;
}
if (!units.Contains(splitEntry[0]) || !units.Contains(splitEntry[1]))
{
Console.WriteLine("Error, measurement unit not recognised.");
}
Above I have a text file that contains types of unit measurement (pounds, ounces, miles and such), the text from this file is split into a string array.
The user enters two measurement units in the following format to convert to two units:
unit,unit,amount
In the text file, the conversion amount for two units is every third split string, like so:
unit,unit,2.80
unit,unit,1.27 (etc)
Is there a way of grouping each set of units and their conversion amounts? For example, if the user tries to convert two particular units, the program knows which conversion value to use when calculating the final result.
Might be a little vague, but it's difficult to explain.
EDIT: The user does not interact with the file, the program simply pulls the data from the file, which is then split into strings (',') and stored in an array.
If I don't got you wrong, the following code should fulfill your requirements (it's very basic, no error handling etc.):
public enum Unit
{
Pound,
Kilo,
Kilometer,
Mile
}
public class UnitMapping
{
public UnitMapping(Unit source, Unit target, double factor)
{
SourceUnit = source;
TargetUnit = target;
Factor = factor;
}
public Unit SourceUnit { get; private set; }
public Unit TargetUnit { get; private set; }
public double Factor { get; private set; }
}
public class UnitCalculator
{
public const string FILE_INPUT = #"Kilo,Pound,0.45359237
Kilometer,Mile,1.609344";
private List<UnitMapping> mappings;
public UnitCalculator()
{
this.mappings = new List<UnitMapping>();
// parse the mappings
foreach (var line in FILE_INPUT.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
{
var fields = line.Split(',');
var source = (Unit)Enum.Parse(typeof(Unit), fields[0]);
var target = (Unit)Enum.Parse(typeof(Unit), fields[1]);
double factor = double.Parse(fields[2], CultureInfo.InvariantCulture);
this.mappings.Add(new UnitMapping(source, target, factor));
}
}
public double Convert(Unit source, Unit target, double value)
{
foreach (var mapping in this.mappings)
{
if (mapping.SourceUnit == source && mapping.TargetUnit == target)
{
return value * mapping.Factor;
}
else if (mapping.SourceUnit == target && mapping.TargetUnit == source)
{
return value * (1 / mapping.Factor);
}
}
throw new InvalidOperationException("No mapping could be found for this conversion.");
}
}
Invoke it like this:
static void Main(string[] args)
{
var calc = new UnitCalculator();
Console.WriteLine(calc.Convert(Unit.Mile, Unit.Kilometer, 1));
}
If you don't know the units, you can use strings as well.

Categories

Resources