I want to take a List of strings with around 12 objects and split it into two List of strings but completely randomise it.
Example of List:
List 1:
EXAMPLE 1
EXAMPLE 2
EXAMPLE 3
EXAMPLE 4
EXAMPLE 5
EXAMPLE 6
EXAMPLE 7
EXAMPLE 8
Apply some logic here...
Result gives me two lists: List 1:
EXAMPLE 5
EXAMPLE 6
EXAMPLE 1
EXAMPLE 8
List 2:
EXAMPLE 2
EXAMPLE 3
EXAMPLE 4
EXAMPLE 7
I'm a newbie to C# MVC, so I've found some answers on Stack but none have been able to answer my question.
Edit: What I've tried so far gives me one random member of the team. I want to now expand on this and create the two lists as mentioned above.
[HttpPost]
public ActionResult Result(Models.TeamGenerator model)
{
var FormNames = model.Names;
string[] lines = FormNames.Split(
new[] { Environment.NewLine },
StringSplitOptions.None);
List<string> listOfLines = new List<string>();
foreach (var i in lines)
{
listOfLines.Add(i);
}
string[] result1 = listOfLines.Where(item => item != string.Empty).ToArray();
Random genRandoms = new Random();
int aRandomTeam = genRandoms.Next(listOfLines.Count);
string currName = listOfLines[aRandomTeam];
return View();
}
*EDIT** Thanks for the solution! I've now got my application working and managed to publish it to the web, https://www.teamgenerator.online.
true
, the other half with false
.true
, include that element in the first list; otherwise include it in the second list.This approach keeps the items in the same order as they were in the original array, if that is important. (If not, just shuffle the entire array of strings and take the first half and the second half).
Sample code:
using System;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
public static void Main()
{
var strings = Enumerable.Range(1, 20).Select(i => i.ToString()).ToList();
var rng = new Random();
int n = strings.Count;
var include = // Create array of bools where half the elements are true and half are false
Enumerable.Repeat(true, n/2) // First half is true
.Concat(Enumerable.Repeat(false, n-n/2)) // Second half is false
.OrderBy(_ => rng.Next()) // Shuffle
.ToArray();
var list1 = strings.Where((s, i) => include[i]).ToList(); // Take elements where `include[index]` is true
var list2 = strings.Where((s, i) => !include[i]).ToList(); // Take elements where `include[index]` is false
Console.WriteLine(string.Join(", ", list1));
Console.WriteLine(string.Join(", ", list2));
}
}
}
Here's a completely different approach that uses a modified version of a standard algorithm for selecting K items from N items (in this case, K = N/2):
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
public static void Main()
{
var strings = Enumerable.Range(1, 20).Select(n => n.ToString()).ToList();
var list1 = new List<string>();
var list2 = new List<string>();
var rng = new Random();
int available = strings.Count;
int remaining = available / 2;
foreach (var s in strings)
{
if (rng.NextDouble() < remaining / (double) available)
{
list1.Add(s);
--remaining;
}
else
{
list2.Add(s);
}
--available;
}
Console.WriteLine(string.Join(", ", list1));
Console.WriteLine(string.Join(", ", list2));
}
}
}
This approach is much more performant than my first solution, but since your list is only about 12 items long, this is hardly important for your problem.
You're currently only generating one random number and getting one value. What you need to do is put that into a loop that is run half as many times as there are items in the list.
var genRandoms = new Random();
var numberRequired = listOfLines.Count/2;
var output = new List<string>();
for (var i=0; i<numberRequired; i++)
{
var aRandomTeam = genRandoms.Next(listOfLines.Count);
output.Add(listOfLines[aRandomTeam]);
listOfLines.RemoveAt(aRandomTeam);
}
Also, this bit at the beginning:
string[] lines = FormNames.Split(
new[] { Environment.NewLine },
StringSplitOptions.None);
List<string> listOfLines = new List<string>();
foreach (var i in lines)
{
listOfLines.Add(i);
}
string[] result1 = listOfLines.Where(item => item != string.Empty).ToArray();
Can be rewritten as:
var listOfLines = FormNames.Split(
new[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries).ToList();
Removing the empty items as part of the split, and using a built-in method to convert it to a list.
First, try to shuffle the list using the Random
function
static class MyExtensions
{
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
then split the list into two using Linq
static void Main(String[] args)
{
List<string> examples = new List<string>();
for(int i=1;i<=12;i++)
{
examples.Add($"Example {i}");
}
examples.Shuffle();
var firstlist = examples.Take(examples.ToArray().Length / 2).ToArray();
Console.WriteLine(String.Join(", ", firstlist));
var secondlist = examples.Skip(examples.ToArray().Length / 2).ToArray();
Console.WriteLine(String.Join(", ", secondlist));
Console.ReadLine();
}
the output looks like this
Example 6, Example 8, Example 3, Example 9, Example 5, Example 2
Example 10, Example 11, Example 4, Example 7, Example 12, Example 1
So I wrote an example in a console app, but the concept works just the same... See comments in code block below
Sample List
var fullList = new List<string>()
{
"ITEM 01", "ITEM 02", "ITEM 03", "ITEM 04", "ITEM 05", "ITEM 06",
"ITEM 07", "ITEM 08", "ITEM 09", "ITEM 10", "ITEM 11", "ITEM 12"
};
Initialize Two Lists to Split Values
var list1 = new List<string>();
var list2 = new List<string>();
Creating Two Random Lists
// Initialize one Random object to use throughout the loop
var random = new Random();
// Note: Start at count and count down because we will alter the count of the list
// so counting up is going to mess up. Ex: Count = 4, Remove 1 (Count = 3), Loop won't go to 4
for(int i = fullList.Count; i > 0; i--)
{
// Pull random index
var randomIndex = random.Next(fullList.Count);
// Pull item at random index
var listItem = fullList[randomIndex];
// If i is even, put it in list 1, else put it in list 2.
// You could do whatever you need to choose a list to put it
if (i % 2 == 0)
list1.Add(listItem);
else
list2.Add(listItem);
// Remove random item from the full list so it doesn't get chosen again
fullList.RemoveAt(randomIndex);
}
Results
Console.WriteLine("LIST 1");
Console.WriteLine(string.Join(Environment.NewLine, list1));
Console.WriteLine();
Console.WriteLine("LIST 2");
Console.WriteLine(string.Join(Environment.NewLine, list2));
-----------------------
LIST 1
ITEM 05
ITEM 04
ITEM 12
ITEM 11
ITEM 08
ITEM 01
LIST 2
ITEM 02
ITEM 03
ITEM 09
ITEM 06
ITEM 10
ITEM 07
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With