Reputation: 117
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.
Upvotes: 5
Views: 1131
Reputation: 109547
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.
Upvotes: 3
Reputation: 388
Here's a simple solution that falls in line with how you were attempting to solve the problem.
The main logic is as followed:
while there are still items in the master list:
choose a random number [0,list.count) as the current target index
choose a random number [0,1] as the current target list to add to
add the item chosen randomly to the randomly selected list
remove the item chosen from the master list
Here's the code:
var random = new Random();
var list = new List<string> { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
var newList1 = new List<string>();
var newList2 = new List<string>();
while(list.Count > 0)
{
//choose the index randomly
int index = random.Next(list.Count);
//get the item at the randomly chosen index
string curItem = list[index];
//choose the list randomly(1==newList1, 2==newList2)
int listChoice = random.Next(2);
//Add the item to the correct list
if(listChoice == 1)
{
newList1.Add(curItem);
}
else
{
newList2.Add(curItem);
}
//finally, remove the element from the string
list.RemoveAt(index);
}
Upvotes: 0
Reputation: 2153
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
Upvotes: 1
Reputation: 3231
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.
Upvotes: 2
Reputation: 4250
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
Upvotes: 2