Which collection type to use? - c#

I have a scenario where I have a list of classes, and I want to mix up the order. For example:
private List<Question> myQuestions = new List<Question>();
So, given that this is now populated with a set of data, I want to mix up the order. My first thought was to create a collection of integers numbered from 1 to myQuestions.Count, assign one at random to each question and then loop through them in order; however, I can’t seem to find a suitable collection type to use for this. An example of what I mean would be something like this:
for (int i = 0; i <= myQuestions.Count -1; i++)
tempCollection[i] = myQuestions[rnd.Next(myQuestions.Count-1)];
But I’m not sure what tempCollection should be – it just needs to be a single value that I can remove as I use it. Does anyone have any suggestions as to which type to use, or of a better way to do this?

I suggest you copy the results into a new List<Question> and then shuffle that list.
However, I would use a Fisher-Yates shuffle rather than the one you've given here. There are plenty of C# examples of that on this site.
For example, you might do:
// Don't create a new instance of Random each time. That's a detail
// for another question though.
Random rng = GetAppropriateRandomInstanceForThread();
List<Question> shuffled = new List<Question>(myQuestions);
for (int i = shuffled.Count - 1; i > 0; i--)
{
// Swap element "i" with a random earlier element it (or itself)
int swapIndex = rng.Next(i + 1);
Question tmp = shuffled[i];
shuffled[i] = shuffled[swapIndex];
shuffled[swapIndex] = tmp;
}

You could use Linq and order by a random value:
List<string> items = new List<string>();
items.Add("Foo");
items.Add("Bar");
items.Add("Baz");
foreach (string item in items.OrderBy(c => Guid.NewGuid()))
{
Console.WriteLine(item);
}

temp Collection should be same type as myQuestions.
I would also suggest a change in your code:
for (int i = 0; i <= myQuestions.Count -1; i++)
to
for (int i = 0; i < myQuestions.Count; i++)
Does the same thing, but this is how most programers do it so it will make your code simpler to read.

Related

Random objects to a list without repetition

I have a little problem over here. I have a list of questions KlausimuList and i have to form another list Atsitiktinis witch would be a random length and would have random questions taken from KlausimuList. My method works, but the problem is - questions are duplicating. Could you please write me a code where they don't? I have an idea of making a seperate int array of indexes of questions i already added, and then each time check if that question already is in that list. If it does - genererate a new number, but i just don't know how to write the code for this thing :D. Thanks in advice. Code is in c#.
static void FormuotiAtsisiktini(List<Klausimas> KlausimuList, ref List<Klausimas> Atsitiktinis)
{
Random kiek = new Random();
int kiekis = kiek.Next(1, KlausimuList.Count);
for (int i = 0; i < kiekis; i++)
Atsitiktinis.Add(KlausimuList[kiek.Next(1, KlausimuList.Count)]);
}
You could use HashSet to avoid duplicates. Add method on HashSet returns false when try adding duplicate item.
static void FormuotiAtsisiktini(List<Klausimas> KlausimuList, ref List<Klausimas> Atsitiktinis)
{
Random kiek = new Random();
int kiekis = kiek.Next(1, KlausimuList.Count);
HashSet<Klausimas> hashset= new HashSet<Klausimas>();
for (int i = 0; i < kiekis;)
{
i+= hashset.Add(KlausimuList[kiek.Next(1, KlausimuList.Count)])? 1:0; // returns true when successfully added.
}
Atsitiktinis = hashset.ToList();
}
This should work. It creates a copy of the original list and removes the already taken items from it:
static List<Klausimas> FormuotiAtsisiktini(List<Klausimas> KlausimuList)
{
Random kiek = new Random();
List<Klausimas> source = new List<Klausimas>(KlausimuList);
List<Klausimas> result = new List<Klausimas>();
int kiekis = kiek.Next(1, KlausimuList.Count);
for (int i = 0; i < kiekis; i++)
{
var match = source[kiek.Next(0, source.Count - 1)];
result.Add(match);
source.Remove(match);
}
return result;
}
What you are describing is called sampling without replacement, and a solution to that is provided in this SO post. Furthermore, to ensure that you are actually adding duplicates to your collection, consider using a HashSet instead of a List.

Binary search slower, what am I doing wrong?

EDIT: so it looks like this is normal behavior, so can anyone just recommend a faster way to do these numerous intersections?
so my problem is this. I have 8000 lists (strings in each list). For each list (ranging from size 50 to 400), I'm comparing it to every other list and performing a calculation based on the intersection number. So I'll do
list1(intersect)list1= number
list1(intersect)list2= number
list1(intersect)list888= number
And I do this for every list. Previously, I had HashList and my code was essentially this: (well, I was actually searching through properties of an object, so I
had to modify the code a bit, but it's basically this:
I have my two versions below, but if anyone knows anything faster, please let me know!
Loop through AllLists, getting each list, starting with list1, and then do this:
foreach (List list in AllLists)
{
if (list1_length < list_length) //just a check to so I'm looping through the
//smaller list
{
foreach (string word in list1)
{
if (block.generator_list.Contains(word))
{
//simple integer count
}
}
}
// a little more code, but the same, but looping through the other list if it's smaller/bigger
Then I make the lists into regular lists, and applied Sort(), which changed my code to
foreach (List list in AllLists)
{
if (list1_length < list_length) //just a check to so I'm looping through the
//smaller list
{
for (int i = 0; i < list1_length; i++)
{
var test = list.BinarySearch(list1[i]);
if (test > -1)
{
//simple integer count
}
}
}
The first version takes about 6 seconds, the other one takes more than 20 (I just stop there cuz otherwise it would take more than a minute!!!) (and this is for a smallish subset of the data)
I'm sure there's a drastic mistake somewhere, but I can't find it.
Well I have tried three distinct methods for achieving this (assuming I understood the problem correctly). Please note I have used HashSet<int> in order to more easily generate random input.
setting up:
List<HashSet<int>> allSets = new List<HashSet<int>>();
Random rand = new Random();
for(int i = 0; i < 8000; ++i) {
HashSet<int> ints = new HashSet<int>();
for(int j = 0; j < rand.Next(50, 400); ++j) {
ints.Add(rand.Next(0, 1000));
}
allSets.Add(ints);
}
the three methods I checked (code is what runs in the inner loop):
the loop:
note that you are getting duplicated results in your code (intersecting set A with set B and later intersecting set B with set A).
It won't affect your performance thanks to the list length check you are doing. But iterating this way is clearer.
for(int i = 0; i < allSets.Count; ++i) {
for(int j = i + 1; j < allSets.Count; ++j) {
}
}
first method:
used IEnumerable.Intersect() to get the intersection with the other list and checked IEnumerable.Count() to get the size of the intersection.
var intersect = allSets[i].Intersect(allSets[j]);
count = intersect.Count();
this was the slowest one averaging 177s
second method:
cloned the smaller set of the two sets I was intersecting, then used ISet.IntersectWith() and checked the resulting sets Count.
HashSet<int> intersect;
HashSet<int> intersectWith;
if(allSets[i].Count < allSets[j].Count) {
intersect = new HashSet<int>(allSets[i]);
intersectWith = allSets[j];
} else {
intersect = new HashSet<int>(allSets[j]);
intersectWith = allSets[i];
}
intersect.IntersectWith(intersectWith);
count = intersect.Count;
}
}
this one was slightly faster, averaging 154s
third method:
did something very similar to what you did iterated over the shorter set and checked ISet.Contains on the longer set.
for(int i = 0; i < allSets.Count; ++i) {
for(int j = i + 1; j < allSets.Count; ++j) {
count = 0;
if(allSets[i].Count < allSets[j].Count) {
loopingSet = allSets[i];
containsSet = allSets[j];
} else {
loopingSet = allSets[j];
containsSet = allSets[i];
}
foreach(int k in loopingSet) {
if(containsSet.Contains(k)) {
++count;
}
}
}
}
this method was by far the fastest (as expected), averaging 66s
conclusion
the method you're using is the fastest of these three. I certainly can't think of a faster single threaded way to do this. Perhaps there is a better concurrent solution.
I've found that one of the most important considerations in iterating/searching any kind of collection is to choose the collection type very carefully. To iterate through a normal collection for your purposes will not be the most optimal. Try using something like:
System.Collections.Generic.HashSet<T>
Using the Contains() method while iterating over the shorter list of two (as you mentioned you're already doing) should give close to O(1) performance, the same as key lookups in the generic Dictionary type.

Incrementing concatenated list name

Say for example I have a number (which will change each run time) assigned to the following variable:
var number = 3;
I would like to create this many lists, whose names have a number at the end which increments every time, as follows:
list_0
list_1
list_2
I am declaring a list in the following standard format:
List<double> list = new List<double>();
I assume you would use a loop, but I am unsure as to how to correctly assign the name dynamically. Here's what I have:
for (var i = 0; i < number; i++)
{
List<double> ("list_" + i) = new List<double>();
}
Any help would be appreciated
This isn't possible the way you intend to, although you might have some luck using the Reflection API.
This is an anti-pattern though: you should know before compile time what your lists are intending to do and give them a name that describes their purpose. If you don't, you might want to not name them and simply keep their references in a list, like this:
List<List<Double> myLists = new List<List<double>>();
var listX = new List<double>(new[]{5.0, 2.5, 3.2});
myLists.add(listX);
Which will make it a lot easier for you to work with them.
What you're trying to do is a bad practice. Most of the code written nowadays is already hard enough to read, if you try to add variables into variables names (and I'm happy the compiler forbids this) it would be truly infernal.
Instead, think of a data structure that would fit your needs; you need to have multiple variables, ordered from 0 to X. Sounds like a list to me, so you can create a List of List instead.
So if you have int number = 3; you can do
List<List<double>> lists = new List<List<double>>();
for (int i = 0; i < number; i++)
{
lists[i] = new List<double>();
}
If you want to access one of your lists after that, you easily can use the index or the LINQ .ElementAt()
List<double> listNumberTwo = lists[2];
List<double> listNumberTwo = lists.ElementAt(2);

Using a string in place of an object when accessing it

First off I'm sure that I am using the wrong terminology here but I will fix it if someone comments on it. Please be gentle.
So I have multiple charts on a page and I am performing virtually identical actions on each. For demonstrative purposes lets call my charts something like: chart1, chart2, ..., chartn where n is somewhere in the vicinity of 20. What I would like to do is drop this in a for loop and perform all the work in one smaller chunk of code, especially if I have to tweak it later.
So my question is whether or not I can vary the n part representing the object (terminology?) so I can get this done more efficiently.
i.e.:
for(int i = 0; i < 20; i++)
{
String chartName = "chart" + i;
chartName.Series.Clear();
}
I have a feeling you can't do this with a string so I was looking into doing a foreach but I don't know how to do this with charts.
Thanks a lot!
You should put the charts in a list. For example, this makes a list of Chart objects (or whatever your chart type is):
List<Chart> charts = new List<Chart>();
Then you can add charts:
charts.Add(new Chart());
And use them:
for (int i = 0; i < charts.Count; i++)
{
charts[i].Series.Clear();
}
Of course, you can make the charts variable a field in your class.
You can directly initialize a list (or array, or dictionary1) like this:
List<Chart> charts = new List<Charts>()
{
new Chart(),
new Chart(),
existingChart1,
existingChart2
};
Or, if you create a new array of objects using that syntax...
Chart[] arrayOfCharts = new []
{
new Chart(),
new Chart(),
existingChart1,
existingChart2
};
...then you can add multiple objects at once using AddRange:
charts.AddRange(arrayOfCharts);
1) You can use this so-called collection initializer syntax on any object that has a public Add method.
Can you access your chart from a list/array/collection of charts like this?
for (int i = 0; i <= 19; i++) {
String chartName = "chart" + i;
Charts(chartName).Series.Clear();
}
or maybe
for (int i = 0; i <= 19; i++) {
String chartName = "chart" + i;
Charts(i).Series.Clear();
}

How do I set parts of an array to a random value in C#?

I'm new to programming so please bear with me!
I am trying to set the parts of an array to random values, but whenever I run the program it sets all parts of the array to the same value. I want them all to be different.
Here is my code:
int[] hello_array = new int[10];
Console.WriteLine("Here");
Random rndm = new Random();
for (int j = 0; j < hello_array.Length; j++)
{
hello_array[j] = rndm.Next(99);
}
Console.WriteLine("Now Here");
for (int i = 0; i < hello_array.Length; i++)
{
Console.WriteLine("hahahaha look at this " + hello_array[0]);
I'm probably completely missing it, but I can't tell what's wrong with my code! Can someone please tell me how I would make it so that all ten parts of the array generate different random numbers?
Change this:
Console.WriteLine("hahahaha look at this " + hello_array[0]);
to this:
Console.WriteLine("hahahaha look at this " + hello_array[i]);
You were printing the same element in the array on every loop.
With the same number of lines, you could use some of the newer C# features to accomplish the same result (but without all the messy details).
You'd define a generic method that takes two parameters: a lambda expression (for instance, () => random.Next(99)) and the number of elements to generate. Inside the method, you'd use the yield keyword to return a new element generated by the factory.
Random random = new Random();
foreach (var element in Generate(() => random.Next(99), 10))
{
Console.WriteLine(element);
}
public static IEnumerable<T> Generate<T>(Func<T> factory, int elements)
{
for (int generated = 0; generated < elements; generated++)
{
yield return factory();
}
}

Categories

Resources