Add one item multiple times to same List - c#

What I am trying to achieve is to add one item to a List, multiple times without using a loop.
I am going to add 50 numbers to a List and want all of those number to be equal to, let's say, 42. I am aware that I can simply create a small loop that runs 50 times and adds the same item over and over again, as such;
List<int> listFullOfInts = new List<int>();
int addThis = 42;
for(int i = 0; i < 50; i++)
listFullOfInts.Add(addThis);
What I am trying to do is something on the lines of;
listFullOfInts.AddRange(addThis, 50);
Or something that is similar to this at least, maybe using Linq? I have a vague memory of seeing how to do this but am unable to find it. Any ideas?

You can use Repeat:
List<int> listFullOfInts = Enumerable.Repeat(42, 50).ToList();
Demo
If you already have a list and you don't want to create a new one with ToList:
listFullOfInts.AddRange(Enumerable.Repeat(42, 50));
If you want to do add reference types without repeating the same reference, you can use Enumerable.Range+Select:
List<SomeClass> itemList = Enumerable.Range(0, 50)
.Select(i => new SomeClass())
.ToList();

You can't do it directly with LINQ since LINQ is side effect free but you can use some of what's found in the System.linq namespace to build the required.
public static void AddRepeated<T>(this List<T> self,T item, int count){
var temp = Enumerable.Repeat(item,count);
self.AddRange(temp);
}
you can then use that as you propose in your post
listFullOfInts.AddRepeated(addThis, 50);

Related

Questions about 2D lists C#

I've ran into an issue with lists as I need to create a 2-dimensional list where I can read data by giving the columns and rows, so I could read from my list by using my_List[col][row] so is it possible making a 2D list that way?
How much performance impact can this have and anything I should be aware that could have a performance impact on the code? I might need to read a few hundred times per second from my 2D list
Is it possible to have a more grid type 2D list so if i have data in 3, 4 and 5 but i dont have anything in 0, 1, and 2 think of it like coordinates. so can I read from the list using myList[3][5] and get the data from there with 0, 1 and 2 having nothing? or do i need to loop it through and add something there like null?
thanks in advance!
Yes, you can indeed use multidimensional arrays or jagged arrays for storing "2D data".
As for creating a data structure that doesn't use any memory space for unused indexes, an option could be to use a dictionary where the keys are tuples of two numbers, like this (assuming that your data are strings):
var items = new Dictionary<(int, int), string>();
items.Add((0,1), "0-1"); //this throws an error if the key already exists
items[(2,3)] = "2-3"; //this silently replaces the value if the key already exists
Console.WriteLine(items.Keys.Contains((0,1))); //true
Console.WriteLine(items.Keys.Contains((0,2))); //false
Console.WriteLine(items[(2,3)]); //"2-3"
Of course you probably want to encapsulate this functionality in its own class, but you get the idea.
Note however that this dictionary approach will probably be worse than a plain array in terms of performance, but it's up to you to experiment and collect some metrics.
You can create 2D Arrays like this :
string[,] twoDArray = new string[2,2];
Then you can loop through it like :
for (int i = 0; i < twoDArray.Length; i++)
{
foreach (int j in twoDArray[i,0])
{
}
}
You can also create 2D Lists like this:
List<List<string>> grid = new List<List<string>>();
and iterate through them using an Enumerator and for example a for loop:
var enu = grid.GetEnumerator();
while (enu.MoveNext())
{
for(int i = 0; i < enu.Current.Count; i++)
{
enu.Current.RemoveAt(i);
}
}
You are basically iterating over all lists and then through each list as long as its size is. Inside the for loop you can alter the encapsuled lists in whatever way you like.

picking random name from text file in c# windows forms non repeatable

I was wondering how get on with this code, currently working on a tournament bracket system.
Currently I have created a comboBox that fetches all the lines from "log.txt" there are 16 lines in the txt file; then I created a assign button that is supposed to assign all the names into 16 textboxes called User1 --> User16, however the same name cant be repeated.
I looked at "Array of list" & "Array of string", but I seem to be stuck since I cant really figure out what to put in the code.
my random button looks like this at the moment:
private void assign_Click(object sender, EventArgs e)
{
int x;
Random rnd = new Random();
x = rnd.Next(0, 16);
User1.Text = comboBox2.Items[x].ToString();
x = rnd.Next(0, 16);
User2.Text = comboBox2.Items[x].ToString();
x = rnd.Next(0, 16);
User3.Text = comboBox2.Items[x].ToString();
x = rnd.Next(0, 16);
User4.Text = comboBox2.Items[x].ToString();
and so on untill i hit
x = rnd.Next(0, 16);
User16.Text = comboBox2.Items[x].ToString();
}
One of the simplest, but not necessarily most efficient, way to do this is to put all your strings into a List<string> and remove them randomly one-by-one. This would work a lot better if you put all your textboxes into a collection as well. For example, given a list of strings called myStrings and a collection of textboxes called myTextboxes, you could:
for (var i=0; i < myStrings.Count; i++)
{
var idx = rnd.Next(0, myStrings.Count);
myTextboxes[i].Text = myStrings[idx]; // Note: we are assuming the two collections have
// the same length
myStrings.RemoveAt(idx);
}
This is very easy to implement and very easy to get right, but it's not terribly efficient (for 16 items, it probably doesn't matter) because your collection is repeatedly resized. For a more efficient approach, first shuffle your strings using the Fisher-Yates shuffle and then just assign the first entry from your shuffled strings to the first textbox, the second to the second, and so on.
You could use a List, and after each assignment you could remove the assigned item from the list. This would prevent duplicates.
http://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx
How about removing each item after selecting it?
Try something like
comboBox1.Items.RemoveAt(x);
After adding it and each time your
x = rnd.Next(0, 16);
code will reduce to
x = rnd.Next(0, 15);
until it reaches zero.
A different approach would be after selecting one randomly loop through all the filled ones (or all in general for simpler code) and check if it is already selected. If already selected get a new one until it's different.
For that you could use an array of textboxes (store what you have in an array) and loop through them like so
for(int i=0;i<16;i++)
if(textBoxArray[i].Text==comboBox2.Items[x].toString()){
chosen=true;
}
But removing them from the combobox is much simpler and much faster as code. If you want them to still appear in your combobox you could simultaneously in a List, get your items from that List and remove it from there.
The user will not see anything.
To accomplish this, is fairly simple.
First, you know there are 16 items in total. You don't need to randomize the list but rather, randomize the index that you use to access the item of the list. This part you know.
In order to avoid repeating items, you need to keep a list of indexes that have already been used. Once you have determined an unused index, that's when you need to access your list.
Example:
class Sample
{
List<int> _usedIndexes;
public Sample()
{
_usedIndexes = new List<int>();
}
public int GetRandomIndex(int s, e)
{
Random rnd = new Random();
//Initialize with a random number
int x = rnd.Next(s, e);
//While the index exists in the list of used indexes, get another random number.
while(_usedIndexes.Exists(index => index == x))
{
x = rnd.Next(s, e);
}
//Add the number to the list of used indexes
_usedIndexes.Add(x);
return x;
}
}
Then you simply access the List of names you have with the index you have acquired as follows:
int unusedIndex = GetRandomIndex(0, 16);
User1.Text = comboBox2.Items[unusedIndex].ToString();

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

How to define an array with equal value in c#?

I want to create a new array. Let's say
int[] clickNum = new int[800];
Then I want to do something like clickNum = 2, which would make all array elements starting from clickNum[0] to clickNum[800], set to 2. I know there's a way to do it by using a loop; but what I am after is just a function or a method to do it.
I suppose you could use Enumerable.Repeat when you initialise the array:
int[] clickNum = Enumerable.Repeat(2, 800).ToArray();
It will of course be slower, but unless you're going to be initiating literally millions of elements, it'll be fine.
A quick benchmark on my machine showed that initialising 1,000,000 elements using a for loop took 2ms, but using Enumerable.Repeat took 9ms.
This page suggests it could be up to 20x slower.
I don't think there's any built-in function to fill an existing array/list.
You could write a simple helper method for that if you need the operation in several places:
static void Fill<T>(IList<T> arrayOrList, T value)
{
for (int i = arrayOrList.Count - 1; i >= 0; i--)
{
arrayOrList[i] = value;
}
}
I guess you are looking for a function you created but you do not have the time to type it. So if you want it in a single line, try:
for(int i = 0; i < clickNum.Length; i++, clickNum[i] = 2);
Using Array.ConvertAll should be more efficient if you are working with very large arrays and performance is a concern:
int[] clickNum = Array.ConvertAll(new int[800], x => x = 2);
And you can also use a standard LINQ Select if performance doesn't worry you:
int[] clickNum = new int[800].Select(x => x = 2).ToArray();

Select items from List of structs

I've got List of sctructs. In struct there is field x. I would like to select those of structs, which are rather close to each other by parameter x. In other words, I'd like to clusterise them by x.
I guess, there should be one-line solution.
Thanks in advance.
If I understood correctly what you want, then you might need to sort your list by the structure's field X.
Look at the GroupBy extension method:
var items = mylist.GroupBy(c => c.X);
This article gives a lot of examples using group by.
If you're doing graph-style clustering, the easiest way to do it is by building up a list of clusters which is initially empty. Then loop over the input and, for each value, find all of the clusters which have at least one element which is close to the current value. All those clusters should then be merged together with the value. If there aren't any, then the value goes into a cluster all by itself.
Here is some sample code for how to do it with a simple list of integers.
IEnumerable<int> input;
int threshold;
List<List<int>> clusters = new List<List<int>>();
foreach(var current in input)
{
// Search the current list of clusters for ones which contain at least one
// entry such that the difference between it and x is less than the threshold
var matchingClusters =
clusters.Where(
cluster => cluster.Any(
val => Math.Abs(current - val) <= threshold)
).ToList();
// Merge all the clusters that were found, plus x, into a new cluster.
// Replace all the existing clusters with this new one.
IEnumerable<int> newCluster = new List<int>(new[] { current });
foreach (var match in matchingClusters)
{
clusters.Remove(match);
newCluster = newCluster.Concat(match);
}
clusters.Add(newCluster.ToList());
}

Categories

Resources