Create two random lists from one list - c#

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.

Create an array of bools the same size as the list of strings.
Fill one half of the array of bools with true, the other half with false.
Shuffle the array of bools.
Iterate through the list of strings. For each element, if the corresponding element in the bool array is 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

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

Related

How do I display a list and a list of lists side by side

I found a code that displays two lists side by side but a list and a list of lists no luck
this is the code of two lists side by side
for (var i = 0; i < bncount; i++)
{
//Console.WriteLine(String.Format("{0,-10} | {1,-10}", hed.ElementAt(i),bin.ElementAt(i)));
Console.WriteLine(String.Format("{0,-10} | {1,-10}", i< hdcount ? hed[i] : string.Empty, i< bncount ? bin[i] : string.Empty));
}
but the string.empty is for lists only and not list of lists and ElementAt() also wouldn't work
I tried using linq with foreach but no success
the hed is a list of strings and bn is a list of lists of a sequence of numbers
my output are as follows
foreach(var r in bin) //0010 1110 1111
foreach(var m in hed) //red blue white
I want to have the following output
red 0010
blue 1110
white 1111
or
red blue white
0 1 1
0 1 1
1 1 1
0 0 1
Any Idea on how to do this in c# in general or in Linq? the methods I tried either resulted on reprinting one value only froma hed and all the vlaues from bin or the opposite
Not sure if I understood the question correctly, I think it would be helpful to extend the code examples including the variables definition. Anyway, if I understood correctly, this would me my approach:
var listOfString = new List<string>( )
{
"red",
"blue",
"white"
};
var listOfArrays = new List<int[]>( )
{
new int[] { 0,0,1,0 },
new int[] { 0,1,1,1 },
new int[] { 1,1,1,1 }
};
// Here you could add a condition in case you are not 100% sure your arrays are of same length.
for( var i = 0; i < listOfString.Count; i++ )
{
var stringItem = listOfString[i];
var arrayItem = listOfArrays[i];
Console.WriteLine( $"{stringItem} {string.Join( null, arrayItem )}" );
}
I would suggest using a different structure for storing your data (considering OOP principles) and the following code for printing the data out:
public class Item
{
public string Name { get; set; }
public List<int> Values { get; set; }
}
public void Print(List<Item> items)
{
foreach (var item in items)
{
Console.WriteLine($"{item.Name} {string.Join("", item.Values)}");
}
}
The first version is not so hard:
string reuslt = string.Join("\n", bin.Zip(hed).Select(x => $"{x.Item1} {x.Item2}"));
With zip, we create an enumerable of tuples, where the n-th tuple has the n-th element of bin and the n-th element of hed. You can just concatenate those two items.
The second version is a bit more complex:
result = string.Join("\t",hed) + "\n" +
string.Join("\n",Enumerable.Range(0, bin.First().Length)
.Select(x => string.Join("\t\t", bin.Select(str => str[x]))));
We create the heading line by just joing the hed strings. Then we create an enumerable of numbers which represent the indexes in the string. The enumerable will be 0, 1, 2, 3. Then we take the char with index 0 of each string of the bin list, then the char with index 1 of each string of the bin list and so on.
Online demo: https://dotnetfiddle.net/eBQ54N
You can use a dictionary:
var hed = new List<string>(){"red", "blue", "white"};
var bin = new List<string>(){ "0010", "1110", "1111" };
Dictionary<string, string> Dic = new Dictionary<string, string>();
for (int i = 0; i < hed.Count; i++)
{
Dic.Add(hed[i],bin[i]);
}
foreach (var item in Dic)
{
Console.WriteLine(item.Key+" "+item.Value);
}
Console.ReadKey();

C# parse & compare huge list/string

I have 2 giant lists (over 2000 each)
And I want to parse & compare them.
What the list looks like:
zone "exampledomain.com" {
zone "exampledomain2.com" {
zone "exampledomain3.com" {
zone "exampledomain4.com" {
zone "exampledomain5.com" {
zone "exampledomain6.com" {
zone "exampledomain7.com" {
What the other list looks like:
zone "exampledomain.com" {
zone "exampledomain3.com" {
zone "exampledomain5.com" {
zone "exampledomain7.com" {
Both lists have this same format of zone "____" {
I want to parse so that I can compare the domains and then get the difference of domains so I know what the other one is missing, they should both have the same results.
I have come across this code:
static void Main(string[] args)
{
string s1 = "i have a car a car";
string s2 = "i have a new car bmw";
List<string> diff;
IEnumerable<string> set1 = s1.Split(' ').Distinct();
IEnumerable<string> set2 = s2.Split(' ').Distinct();
if (set2.Count() > set1.Count())
{
diff = set2.Except(set1).ToList();
}
else
{
diff = set1.Except(set2).ToList();
}
}
But I am wondering what would be the best way to do it considering I have over 2000 lines in each list.
The example you give will only show list 1 with the items from list 2 removed. If you also want what is in list 2 that isn't in list 1 you will have to do two queries
var difference1 = list1.Except(list2);
var difference2 = list2.Except(list1);
I'm not sure what code is involved when Except is executed, but if you'd like to see an implementation of how to generate two lists containing differences then here is one solution:
static void Differerence(
IEnumerable<string> source1, IEnumerable<string> source2,
out List<string> difference1, out List<string> difference2)
{
//Move the data from the sources into ordered queues
var sourceValues1 = new Queue<string>(source1.OrderBy(x => x));
var sourceValues2 = new Queue<string>(source2.OrderBy(x => x));
difference1 = new List<string>();
difference2 = new List<string>();
while(sourceValues1.Count > 0 && sourceValues2.Count > 0)
{
string value1 = sourceValues1.Peek();
string value2 = sourceValues2.Peek();
switch (string.Compare(value1, value2))
{
//If they match then don't add difference to either list
case 0:
sourceValues1.Dequeue();
sourceValues2.Dequeue();
break;
//The left queue has the lowest value, record that and move on
case -1:
difference1.Add(value1);
sourceValues1.Dequeue();
break;
//The right queue has the lowest value, record that and move on
case 1:
difference2.Add(value2);
sourceValues2.Dequeue();
break;
}
}
//At least one of the queues is empty, so everything left in the other queue
difference1.AddRange(sourceValues1);
difference2.AddRange(sourceValues2);
}
I don't know how LINQ could possibly do it any faster, but my routine will deal with duplicates entries like the value "1" in the following example, whereas LINQ won't. So keep that in mind when choosing which to use rather than just the difference in speed.
static void Main(string[] args)
{
var list1 = new string[] { "1", "1", "3", "5", "7", "9" };
var list2 = new string[] { "1", "2", "4", "6", "9", "10" };
var difference1 = list1.Except(list2);
var difference2 = list2.Except(list1);
List<string> differenceX1;
List<string> differenceX2;
Differerence(list1, list2, out differenceX1, out differenceX2);
}
It's easy to combine the two results into one if you need to
var allDifferences = differenceX1.Union(differenceX2);
HashSets are used for lists of unique elements:
https://msdn.microsoft.com/en-us/library/bb359438(v=vs.110).aspx
HashSet<string> uniqueStrings = new HashSet<string>();
foreach (string s1 in list1)
{
uniqueStrings.Add(s1);
}
foreach (string s2 in list2)
{
uniqueStrings.Add(s2);
}

How to remove strings in list from another list?

I have 2 list which names are listA and listB.
I want to remove strings in listB which are in listA, but I want to do this in this way:
if listA contains: "bar", "bar", "bar", "foo"
and listB contains : "bar"
it removes only 1 bar and the result will be:
"bar", "bar", "foo"
the code I wrote removes all "bar":
List<string> result = listA.Except(listB).ToList();
You can try to remove it one by one:
foreach (var word in listB)
listA.Remove(word);
The Remove method will only remove one element at a time and is not throwing exception (but returning false) when the item is not found: https://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx
var listA = new List<string>() { "bar", "bar", "bar", "foo" };
var listB = new List<string>() { "bar" };
foreach (var word in listB){
listA.Remove(word);
}
Here is a more efficient way to do that:
var countB = new Dictionary<string, int>(listB.Count);
foreach (var x in listB)
{
int count;
countB.TryGetValue(x, out count);
countB[x] = count + 1;
}
listA.RemoveAll(x =>
{
int count;
if (!countB.TryGetValue(x, out count)) return false;
if (count == 1)
countB.Remove(x);
else
countB[x] = count - 1;
return true;
});
This is a faster method but it is likely to change the order of elements of first list. Steps:
Map the listA to a Dictionary<string, int> (let's call it listAMap), where key is the element of the list and value is the total number of times that value has occurred in listA;
Iterate through listB and for every element of listB, if that element is in the listAMap, reduce its count;
Get the keys of listMapA using Keys property of C# dictionaries, and iterate through all the keys. For every key which has positive value, add that key to another list a total of its count times. So if an entry is "bar" -> 2, then add "bar" twice in the new list.
Total run time of the algorithm is O(m + n), where m and n are number of elements in both the original lists. It is a better running time than other approaches mentioned here which have O(m * n) running time. Obviously this algorithm uses more space.
Supportive Code for the algorithm above:
//Step-1: Create the dictionary...
var listAMap = new Dictionary<string, int>();
foreach (var listAElement in listA)
{
listAMap.ContainsKey(listAElement) ? listAMap[listAElement]++ : listAMap.Add(listAElement, 1);
}
// Step-2: Remove the listB elements from dictionary...
foreach (var listBElement in listB)
{
if (listAMap.Contains(listBElement)) listAMap[listBElement]--;
}
//Step-3: Create the new list from pruned dictionary...
var prunedListA = new List<string>();
foreach (var key in listAMap.Keys)
{
if (listAMap[key] <= 0) continue;
for (var count = 0; count < listAMap[key]; count++)
{
prunedListA.Add(key);
}
}
//prunedListA contains the desired elements now.

how to populate complex array of list of array structure c#?

I`m getting hard time of trying to populate that structure:
var arrlist = new List<int[]>()[length];
What I want: an array of fixed length of lists.
Each list would contain unknown number of arrays of length 2.
trying to add following list into above array:
var digfaclist = new List<int[]>();
var factors = new int[2] {i, j};
digfaclist.Add(factors);
I am looping through arrlist and populating it with length 2 arrays in lists. After that I am trying to do smth like:
arrlist[0] = digfaclist;
is it even viable or I should go with some other approach? performance/structure-vice.
something like this?
int count = 15;
List<int[]>[] list = new List<int[]>[count];
for (int i = 0; i < count; i++)
{
list[i] = new List<int[]>(); //you have to initialize them
}
list[0].Add(new []{30, 22});
list[0].Add(new []{11, 22});
foreach (int[] numbers in list[0])
{
Console.WriteLine(string.Join(",",numbers)); //test
}
Is this what you are looking for?
var arr = new int[][] {
new int[] { 1, 2 },
new int[] { 3 ,4 },
new int[] { 5, 6 } }
var arrlist = arr.ToList();
Remember internally a list still maintains an array of items.

Need to add each number 0-x to end of line?

I'm making an app and I'm almost done. I just need to know how I can streamread a txt list and foreach line, add numbers 0-x (x will be the number the user puts in the textbox) and add it to a list. So basically, it would be like this
You import a list with 'dog' on one line, 'cat' on another, and 'fish' on the third. You type '5' into the textbox. the app puts all this into a list:
dog1
dog2
dog3
dog4
dog5
cat1
cat2
cat3
cat4
cat5
fish1
fish2
fish3
fish4
fish5
thanks!
The code below should work for you. I assume you can acquire the count value on your own.
var animals = File.ReadAllLines("yourFile.txt"); //new[] {"dog", "cat", "fish"};
var count = 5;
var merged =
from a in animals
from n in Enumerable.Range(1, count)
select a + n;
foreach (var m in merged)
Console.WriteLine(m); //act on each however you want
You can read a text file with File.ReadAllLines. This gives you an array you can iterate over with foreach.
In this foreach loop you can perform another loop from 1 to the number the user entered. int.Parse comes in handy for converting the string the user entered into a number C# can do something with. For the actual iteration you can use a for loop.
You can then add each item to a list.
There is a good example for reading each line in a filestream here: http://msdn.microsoft.com/en-us/library/e4y2dch9.aspx
private List<string> GetThings(string fileName, int count)
{
string[] lines = File.ReadAllLines(fileName);
List<string> result = new List<string>();
foreach (string item in lines)
{
for (int i = 1; i <= count; i++)
result.Add(item + i.ToString());
}
return result;
}
string[] inputList = File.ReadAllLines("yourFile.txt");
List<String> listOfThings = new List<String>();
foreach (string i in inputList)
{
for (int k = 0; k < 5; k++)
{
listOfThings.Add(i + " " + k.ToString());
}
}
then after that, you can print out the list like this:
foreach (string outp in listOfThings)
{
Console.WriteLine(outp);
}
output:
some value 0
some value 1
some value 2
some value 3
some value 4
some other value 0
some other value 1
some other value 2
some other value 3
some other value 4

Categories