array/list management questionon c#, total noob - c#

i am a total noob when it comes to c#
i migrated to a new platform which uses c#, trying to migrate trading rules to new design.
i need to do this for algorithm
here is the question
i have keys array a,b,c,d
i have value set1 3,8,9,10
another value set2 77,89,100,76
these values are related to each other , (a) has values 3,77 and so on
what i need is , i need to filter with value set 2 , for example only values more than 80 then (probably create a new list with the remaining rows) , from the remaining list i need to get the keyname with highest set1 value
i tried it with this probably very bad way,
Array.Sort on one dimensional array value set1, take value[3]
- if this equals 3 then (if value set1 >80 valuefound else take value[2] and repeat
can you show me an easier way , please take my inexperience into account and include as more informatioan and code as possible

You should really start learning more about Linq. C# has very powerful functional-like features that you can do these things really easily with. It's actually really fun :)
Basically, this code does what you want with 3 lines.
var set1 = new[] {3, 8, 9, 10};
var set2 = new[] {77, 89, 100, 76};
var maxFromSet1 = set1
.Zip(set2, (fromSet1, fromSet2) => new {FromSet1 = fromSet1, FromSet2 = fromSet2}) //Match the sets to one another
.Where(zipped => zipped.FromSet2 > 80) // Filter by value
.Max(zipped => zipped.FromSet1); //Gets max

Another way of doing what you want would be to use a Dictionary which holds keys and values together instead of having them in different arrays.
Dictionary<string, int[]> dic = new Dictionary<string, int[]>()
{
{ "a", new[] { 3, 77 } },
{ "b", new[] { 8, 89 } },
{ "c", new[] { 9, 100 } },
{ "d", new[] { 10, 76 } }
};
Then using LINQ you can retrieve the key really easily
string key = dic.Where(x => x.Value[1] > 80) // Filter by second value
.OrderByDescending(x => x.Value[0]) // Order by first value
.First() // Get the max value
.Key; // Get the matching key

Related

Custom Ordering in C#

There is a list of Package item is sorted by GUID, but I need to order them as follows
KK %, AB, AB art, DD %, FV, ER, PP and WW
I have implemented as follows, but I wonder is there a better way of doing it?
List<PackageType> list = new List<PackageType> (8);
foreach (var a in mail.Package)
{
if (a.Name == "KK %")
list[0] = a;
else if (a.Name == "AB art")
list[1] = a;
else if (a.Name == "AB")
list[2] = a;
else if (a.Name == "DD %")
list[3] = a;
else if (a.Name == "FV")
list[4] = a;
else if (a.Name == "ER")
list[5] = a;
else if (a.Name == "PP")
list[6] = a;
else if (a.Name == "WW")
list[7] = a;
}
You can get this down to two lines (one for array definition, one for ordering):
var PackageOrder = new[] { "KK %", "AB", "AB art", "DD %", "FV", "ER", "PP", "WW"};
//...
var list = mail.Package.OrderBy(p => Array.IndexOf(PackageOrder, p.Name)).ToList();
But we can do even better.
The code so far either requires several O(n) lookups into the reference array, or switching to a Dictionary<string,int>, which is O(1) for each lookup for a value of 1 that might be disproportionate to the task. Each package item may need several of these lookups over the course of a sort operation, which means this might be less efficient than you want.
We can get around that like this:
private static string[] Names = new[] { "KK", "AB", "BC", "DD", "FV", "ER", "PP", "WW" };
//...
var list = mail.Package.
Select(p => new {Package = p, Index = Array.IndexOf(Names, p.Name)}).
OrderBy(p => p.Index).
Select(p => p.Package).ToList();
This guarantees only one lookup per package over the course of the sort. The idea is to first create a projection of the original data that also includes an index, then sort by the index, and finally project back to just the original data. Now the only question is whether to use an array or dictionary, which mainly depends on the size of the reference array (for this size data stick with the array, for more than about 15 items, switch to the dictionary; but it varies depending on the GetHashCode() performance of your type).
Of course, there's also YAGNI to consider. For large sets this will typically be much better, but for small data it might not be worth it, or if the data happens to be sorted in a certain lucky ways it can make things slower. It can also make things slower if your are more constrained by memory pressure than cpu time (common on web servers). But in the general sense, it's a step in the right direction.
Finally, I question the need for an actual List<T> here. Just change the declaration to var and remove the .ToList() at the end. Wait to call ToList() or ToArray() until you absolutely need it, and work with the simple IEnumerable<T> until then. This can often greatly improve performance.
In this case (and for reference, I added this paragraph later on), it seems like you only have eight items total, meaning the extra code isn't really saving you anything. With that in mind, I'd just stick with the two-line solution at the top of this answer (when performance doesn't matter, go for less or simpler code).
// List<PackageType> list = ...;
var names = new[] { "KK", "AB", "BC", "DD", "FV", "ER", "PP", "WW" };
var sortedList = list.OrderBy(packageType => Array.IndexOf(names, packageType.Name));
Here's a longer version of the above that explains in more detail what's going on:
// this array contains all recognized keys in the desired order;
private static string[] Names = new[] { "KK", "AB", "BC", "DD", "FV", "ER", "PP", "WW" };
// this helper method will return the index of a `PackageType`'s `Name`
// in the above array, and thus a key by which you can sort `PackageType`s.
static int GetSortingKey(PackageType packageType)
{
var sortingKey = Array.IndexOf(Names, packageType.Name);
if (sortingKey == -1) throw new KeyNotFoundException();
return sortingKey;
}
// this is how you could then sort your `PackageType` objects:
// List<PackageType> list = ...;
IEnumerable<PackageType> sortedList = list.OrderBy(GetSortingKey);
Updated version of #stakx answer. Well I think this should be better solution, if these values are fixed, also can be used elsewhere. Each value in enum has its int value, by which they can be ordered.
public enum Names
{
KK, //0
AB, //1
BC, //2
DD, //3
FV, //4
ER, //5
PP, //6
WW //7
}
var packageList = list.OrderBy(p => p.Name);
UPDATE
Use example for enum in class
public class Package
{
public Names Name { get; set; }
}
If there is need to get string value of this enum, then just use this
Package package - Package class variable
package.Name.ToString();
If you need whole list of enum names (enum key names), you can use Enum class method:
Enum.GetNames(Type enumType) which returns string array with all defined enum key names.
Enum.GetNames(typeof(Names))
An alternative.. (I haven't compiled it)
var indexPositions = new Dictionary<string, int> {
{ "KK", 0 },
{ "AB", 1 },
{ "BC", 2 },
{ "DD", 3 },
{ "FV", 4 },
{ "ER", 5 },
{ "PP", 6 },
{ "WW", 7 }
}
foreach (var package in mail.Package)
{ // access position
int index;
if (!indexPositions.TryGetValue(a.Name, out index)) {
throw new KeyNotFoundException()
}
list[index] = package;
}
In addition to the usual OrderBy Array.IndexOf method, the list can also sorted in-place:
string[] order = { "KK", "AB", "BC", "DD", "FV", "ER", "PP", "WW" };
list.Sort((a, b) => Array.IndexOf(order, a.Name).CompareTo(Array.IndexOf(order, b.Name)));
A bit more advanced O(n)ish alternative:
var lookup = list.ToLookup(x => x.Name);
list = order.SelectMany(x => lookup[x]).ToList();

Extract Key Value Pairs from a named Collection MVC

I am pulling form values from a loosely bound razor form. The reason I am not using strongly bound model is that the payment value fields and categories are dynamic.
The form collection array reaching the controller is as below:
Payment {"1":"120","4":"23","6":"12","8":"120","9":"100"}
I need to split the array like (When PayCatId =1, Pay =120) (When PayCatId =4, Pay=23) etc..
string [] pa =collection["Payment"].Split(char.Parse(","));
string [] pc =collection.AllKeys["Payment"].Split(char.Parse(","));
Then I am trying to save to database using the logic below;
for (var i = 0; i < pa.Length; i++)
{
payment.Pay = Convert.ToDecimal(pa[i]);
payment.PayCatId = Convert.ToInt32(pa[i]); (added:how do i get this value from pair?)
payment.PayDate = DateTime.Now;
db.Payments.Add(payment);
db.SaveChanges();
}
Removed the Error bit as I have been enlightened that that approach is not applicable
I also want to know if this is the right and reliable approach to achieve this objective.
Just loop through two steps at a time instead of one
for (var i = 0; i < pa.Length; i+=2)
{
payment.Pay = Convert.ToDecimal(pa[i]);
payment.PayCatId = Convert.ToInt32(pa[i+1]);
payment.PayDate = DateTime.Now;
db.Payments.Add(payment);
db.SaveChanges();
}
You can use a combination of Split, Select and ToDictionary to do this, see the code:
var srt = "\"1\":\"120\",\"4\":\"23\",\"6\":\"12\",\"8\":\"120\",\"9\":\"100\"";
srt.Split(',')
.Select(x => x.Split(':'))
.ToDictionary(x => int.Parse(x[0].Replace("\"","")), x => int.Parse(x[1].Replace("\"","")))
/*
Output:
Dictionary<int, int>(5)
{
{ 1, 120 },
{ 4, 23 },
{ 6, 12 },
{ 8, 120 },
{ 9, 100 }
}
*/

C# - sorting by a property

I am trying to sort a collection of objects in C# by a custom property.
(For context, I am working with the Twitter API using the Twitterizer library, sorting Direct Messages into conversation view)
Say a custom class has a property named label, where label is a string that is assigned when the class constructor.
I have a Collection (or a List, it doesn't matter) of said classes, and I want to sort them all into separate Lists (or Collections) based on the value of label, and group them together.
At the moment I've been doing this by using a foreach loop and checking the values that way - a horrible waste of CPU time and awful programming, I know. I'm ashamed of it.
Basically I know that all of the data I have is there given to me, and I also know that it should be really easy to sort. It's easy enough for a human to do it with bits of paper, but I just don't know how to do it in C#.
Does anyone have the solution to this? If you need more information and/or context just ask.
Have you tried Linq's OrderBy?
var mySortedList = myCollection.OrderBy(x => x.PropertyName).ToList();
This is still going to loop through the values to sort - there's no way around that. This will at least clean up your code.
You say sorting but it sounds like you're trying to divide up a list of things based on a common value. For that you want GroupBy.
You'll also want ToDictionary to switch from an IGrouping as you'll presumably be wanting key based lookup.
I assume that the elements within each of the output sets will need to be sorted, so check out OrderBy. Since you'll undoubtedly be accessing each list multiple times you'll want to collapse it to a list or an array (you mentioned list) so I used ToList
//Make some test data
var labels = new[] {"A", "B", "C", "D"};
var rawMessages = new List<Message>();
for (var i = 0; i < 15; ++i)
{
rawMessages.Add(new Message
{
Label = labels[i % labels.Length],
Text = "Hi" + i,
Timestamp = DateTime.Now.AddMinutes(i * Math.Pow(-1, i))
});
}
//Group the data up by label
var groupedMessages = rawMessages.GroupBy(message => message.Label);
//Convert to a dictionary for by-label lookup (this gives us a Dictionary<string, List<Message>>)
var messageLookup = groupedMessages.ToDictionary(
//Make the dictionary key the label of the conversation (set of messages)
grouping => grouping.Key,
//Sort the messages in each conversation by their timestamps and convert to a list
messages => messages.OrderBy(message => message.Timestamp).ToList());
//Use the data...
var messagesInConversationA = messageLookup["A"];
var messagesInConversationB = messageLookup["B"];
var messagesInConversationC = messageLookup["C"];
var messagesInConversationD = messageLookup["D"];
It sounds to me like mlorbetske was correct in his interpretation of your question. It sounds like you want to do grouping rather than sorting. I just went at the answer a bit differently
var originalList = new[] { new { Name = "Andy", Label = "Junk" }, new { Name = "Frank", Label = "Junk" }, new { Name = "Lisa", Label = "Trash" } }.ToList();
var myLists = new Dictionary<string, List<Object>>();
originalList.ForEach(x =>
{
if (!myLists.ContainsKey(x.Label))
myLists.Add(x.Label,new List<object>());
myLists[x.Label].Add(x);
});

How to reverse the order of displaying content of a list?

Let's say I have a list of integers.
var myList = new List<int>();
1, 2, 3, 4, 5, ..., 10
Is there any function that allows me to display them in reverse order, i.e.
10, 9, 8, ..., 1
EDIT
public List<Location> SetHierarchyOfLocation(Location location)
{
var list = new List<Location>(7);
var juridiction = location.Juridiction;
if (juridiction > 0)
{
while (juridiction > 0)
{
var loc = repository.GetLocationByID(juridiction);
list.Add(loc);
juridiction = loc.Juridiction;
}
}
return list;
}
Since the list contains location by location, I want to be able to display it by reversed order as well.
Now, when I write return list.Reversed(), I get the error.
Thanks for helping
var reversed = myList.Reverse() should do the trick.
EDIT:
Correction- as the OP found out, List.Reverse works in-place, unlike Enumerable.Reverse. Thus, the answer is simply myList.Reverse(); - you don't assign it to a new variable.
Is there any function that allows me to display them in reverse order, i.e.
It depends if you want to reverse them in place, or merely produce a sequence of values that is the reverse of the underlying sequence without altering the list in place.
If the former, just use List<T>.Reverse.
// myList is List<int>
myList.Reverse();
Console.WriteLine(String.Join(", ", myList));
If the latter, the key is Enumerable.Reverse:
// myList is List<int>
Console.WriteLine(
String.Join(
", ",
myList.AsEnumerable().Reverse()
)
);
for a beautiful one-liner. Be sure you have using System.Linq;.
To do a foreach loop in a List<Location> reversely you should use:
foreach (Location item in myList.Reverse().ToList())
{
// do something ...
}
The .Reverse() is an in-place reverse; it doesn't return a new list. The .ToList() after the .Reverse() do the trick.

Creating a random name generator. How do I accomplish this?

I'm trying to grab a single item from each of the Lists here, and combine them to make a unique name. This is just for kicks. :)
Here are the lists:
List<string> FirstNames = new List<string>()
{
"Sergio",
"Daniel",
"Carolina",
"David",
"Reina",
"Saul",
"Bernard",
"Danny",
"Dimas",
"Yuri",
"Ivan",
"Laura"
};
List<string> LastNamesA = new List<string>()
{
"Tapia",
"Gutierrez",
"Rueda",
"Galviz",
"Yuli",
"Rivera",
"Mamami",
"Saucedo",
"Dominguez",
"Escobar",
"Martin",
"Crespo"
};
List<string> LastNamesB = new List<string>()
{
"Johnson",
"Williams",
"Jones",
"Brown",
"David",
"Miller",
"Wilson",
"Anderson",
"Thomas",
"Jackson",
"White",
"Robinson"
};
I know I get a single item via an index, and I also know that I can use the Random class to generate a random number from 0 to ListFoo.Count.
What I don't know is how to check if a random permutation has already been drawn from the collections.
I've thought about using the tuple class:
List<Tuple<int,int,int>> permutations = new List<Tuple<int,int,int>>();
But I'm having a brainfart here. ;) Any guidance? I'm not really looking for the entire code to this simple problem, just a suggestion or hint.
EDIT
Thanks to the suggestions given here, here what I've come up with. Any room for improvements?
static void Main(string[] args)
{
List<string> FirstNames = new List<string>()
{
"Sergio",
"Daniel",
"Carolina",
"David",
"Reina",
"Saul",
"Bernard",
"Danny",
"Dimas",
"Yuri",
"Ivan",
"Laura"
};
List<string> LastNamesA = new List<string>()
{
"Tapia",
"Gutierrez",
"Rueda",
"Galviz",
"Yuli",
"Rivera",
"Mamami",
"Saucedo",
"Dominguez",
"Escobar",
"Martin",
"Crespo"
};
List<string> LastNamesB = new List<string>()
{
"Johnson",
"Williams",
"Jones",
"Brown",
"David",
"Miller",
"Wilson",
"Anderson",
"Thomas",
"Jackson",
"White",
"Robinson"
};
var permutations = new List<Tuple<int, int, int>>();
List<string> generatedNames = new List<string>();
Random random = new Random();
int a, b, c;
//We want to generate 500 names.
while (permutations.Count < 500)
{
a = random.Next(0, FirstNames.Count);
b = random.Next(0, FirstNames.Count);
c = random.Next(0, FirstNames.Count);
Tuple<int, int, int> tuple = new Tuple<int, int, int>(a, b, c);
if (!permutations.Contains(tuple))
{
permutations.Add(tuple);
}
}
foreach (var tuple in permutations)
{
generatedNames.Add(string.Format("{0} {1} {2}", FirstNames[tuple.Item1],
LastNamesA[tuple.Item2],
LastNamesB[tuple.Item3])
);
}
foreach (var n in generatedNames)
{
Console.WriteLine(n);
}
Console.ReadKey();
}
You are on the right track!
Every time you generate a name, add it to your tuple list
//Create the tuple
Tuple <int, int, int> tuple = new Tuple<int, int, int>(index1, index2, index3)
if(!permutations.Contains(tuple))
{
permutations.Add(tuple);
//Do something else
}
I would think the simplest solution is to just the stuff the assembled name into a HashSet<string> which will ensure the list of created names is unique.
An alternative to the HashSet answer is to build all of the possible combinations in advance, shuffle them, then store them in a Queue, where you can retrieve them one at a time. This will avoid having to check the existing ones every time you build a new one, and will still be random.
This only works if you don't have a large set to begin with, since the work involved in creating the complete list and shuffling it would be huge for a large set of data.
It's really easy to generate them all using LINQ:
var combs =
(from first in FirstNames
from second in LastNamesA
from third in LastNamesB
select new Tuple<string, string, string>(first, second, third)).ToList();
After this, if you need to take unique elements from the list randomly, just shuffle the list and then pick them one-by-one in order.
You can use the Knuth-Fisher-Yates algorithm (that's an in-place shuffle):
Random rand = new Random();
for (int i = combs.Count - 1; i > 0; i--)
{
int n = rand.Next(i + 1);
var mem = combs[i];
combs[i] = combs[n];
combs[n] = mem;
}
I would create a HashSet<int> and store the numeric representation of the picks (eg 135 for first, third and 5th or use 010305) and then check if they are in the set.
Create a new tuple with 3 random digits
Check if permutations contains your new tuple
If not => Add new tuple to the list. If yes, start with point 1 again.

Categories

Resources