Generating Random Unique items from a string list [duplicate] - c#

This question already has answers here:
Randomize a List<T>
(28 answers)
Closed 3 years ago.
I have string list of a deck of card the strings are as such, A-DIAMONDS, 2-CLUBS, etc.
I want to be able to generate 5 unique items from this list randomly.
I know how to do this in python with Random.sample(5) but in trying to find a solution in C#. everything seems to be generating a random, put it in a list, generate another random, check it against the list and it is working fine.
Is there a more compact way of doing this in C#?
Here is my full code after using Linq for shuffling.
class Program
{
static void Main(string[] args)
{
string [] cardValues = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };
string [] cardSuites = { "HEARTS", "CLUBS", "DIAMONDS", "SPADES" };
List<string> deckOfCards = new List<string>();
foreach(string cardsuit in cardSuites)
{
foreach(string cardvalues in cardValues)
{
deckOfCards.Add(cardvalues + "-" + cardsuit);
}
}
for(int i = 0; i <= 10; i++)
{
List<string> pokerHand = new List<string>();
Random rand = new Random();
deckOfCards = deckOfCards.Select(x => new { card = x, rand = rand.Next() }).OrderBy(x => x.rand).Select(x => x.card).ToList();
for(int x = 0; x < 5; x++)
{
pokerHand.Add(deckOfCards[x]);
}
Console.WriteLine(String.Join(", ", pokerHand));
}
Console.ReadLine();
}
}
}

Here is one way to shuffle using linq. The 5 random cards are the first 5 items in the list. :
class Program
{
static void Main(string[] args)
{
List<string> deck = new List<string>() {
"S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "ST", "SJ", "SQ", "SK", "SA",
"H2", "H3", "H4", "H5", "H6", "H7", "H8", "H9", "HT", "HJ", "HQ", "HK", "HA",
"C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CT", "CJ", "CQ", "CK", "CA",
"D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DT", "DJ", "DQ", "DK", "DA"
};
Random rand = new Random();
deck = deck.Select(x => new { card = x, rand = rand.Next() }).OrderBy(x => x.rand).Select(x => x.card).ToList();
}
}

You could use MoreLINQ. Simply install it from NuGet.
MoreLINQ gives you a Shuffle method. So you can do something like the following:
List<Card> deck = GetDeck();
List<Card> randomFiveCards = deck.Shuffle().Take(5).ToList();

A valid and efficient algorithm is to pick a random index between 0 and n-1 (where n is the number of cards), exchange the last card with the card at that index. Then pick a random index between 0 and n-2 and exchange second-last card with the card at that index.
Repeat other three times with n-3, n-4 and n-5 and your five randomly chosen cards will be at the end of the array.

Related

How to out all the int values from a string array and store that int values in another array

There are words and int values in a string array. My goal is to retrieve the int values from another array. I have seen two methods for converting and finding int values from a string array.
To get all the int values from the string, I first create another int array and use an Array.ConvertAll init. The second method is to loop through isDigit().
Now, the problem is that I cannot find a way to store that value in an int array that has been converted to an int. The final goal is to find the minimum and maximum value from the converted values.
string[] strArray1 = { "Bautik", "99", "Sagar", "3", "Tisha", "12", "Riya", "109" };
int result;
int[] num = Array.ConvertAll(strArray1, x =>
{
bool convertStrIntoInt = int.TryParse(x, out int result);
return result;
});
In here, I don't how to get the result outside of it to do a sort to find min and max values.
I don't understand what you want but if you want only int then use this
List<int> list = new List<int>();
string[] strArray1 = { "Bautik", "99", "Sagar", "3", "Tisha", "12", "Riya", "109" };
foreach (string str in strArray1)
{
if (int.TryParse(str, out int val))
{
list.Add(val);
}
}
int[] vs = list.ToArray();
now all the int values are stored in vs array, if there are some words which contains the int value and you want them to be extracted you can use regex to find the digits in word also
I'd use LINQ for this:
string[] strArray1 = { "Bautik", "99", "Sagar", "3", "Tisha", "12", "Riya", "109" };
int[] num =
(
from x in strArray1
let n = int.TryParse(x, out int n) ? (int?)n : null
where n.HasValue
select n.Value
).ToArray();
That outputs:
Then you can do this:
int max = num.Max();
int min = num.Min();
Little optimized solution could be something like below
string[] strArray1 = { "Bautik", "99", "Sagar", "3", "Tisha", "12", "Riya", "109" };
var numArray = strArray1.Where(var=> new Regex(#"^\d+").IsMatch(var)).ToArray();
var res = Array.ConvertAll(numArray,s => int.Parse(s));
If we don't want to use Regex can try something like below
string[] strArray1 = { "Bautik", "99", "Sagar", "3", "Tisha", "12", "Riya", "109" };
int testNum;
var numArray = strArray1.Where(var=> int.TryParse(var,out testNum)).ToArray();
var res = Array.ConvertAll(numArray,s => int.Parse(s));
You can then get max and min value using
res.Min(), res.Max()
This should give you a starting point for your problem
You can try Regex like below :
string[] strArray1 = {"Bautik", "99", "Sagar", "3", "Tisha", "12", "Riya", "109"};
int[] num = strArray1.Where(v => Regex.IsMatch(v, #"^\d+$")).Select(int.Parse).ToArray();
int min = num.Min();
int max = num.Max();
As you don't want to use Regex, you can go with your solution, just do a small modification to get your desired result. Try this :
string[] strArray1 = { "Bautik", "99", "Sagar", "3", "Tisha", "12", "Riya", "109" };
int[] num = Array.ConvertAll(strArray1, x =>
{
bool convertStrIntoInt = int.TryParse(x, out int result);
return result;
}).Where(x => x > 0).ToArray();
int max = num.OrderByDescending(c => c).ToArray()[0];
int min = num.OrderBy(c => c).ToArray()[0];
Here is another way to get int values from a string array into an int array.
Try it on dotnetfiddle.
public static class Extensions
{
public static int[] ToIntegerArray(this string[] sender)
{
var intArray = Array
.ConvertAll(sender,
(input) => new {
IsInteger = int.TryParse(input, out var integerValue),
Value = integerValue
})
.Where(result => result.IsInteger)
.Select(result => result.Value)
.ToArray();
return intArray;
}
}
Use
string[] strArray1 = { "Bautik", "99", "Sagar", "3", "Tisha", "12", "Riya", "109" };
int[] intArray = strArray1.ToIntegerArray();
Array.Sort(intArray);
foreach (var value in intArray)
{
Console.WriteLine(value);
}
Console.WriteLine($"Min: {intArray.Min()}");
Console.WriteLine($"Max: {intArray.Max()}");
Targeting your intial question:
I don't [know] how to get the result outside of it to do a sort to find min and max values.
You simply can't get a complete solution for your Problem using ConvertAll() with a "normal" int.
So if we take a look at your result:
int[] num = Array.ConvertAll(strArray1, x =>
{
bool convertStrIntoInt = int.TryParse(x, out int result);
return result;
});
// num: int[8] { 0, 99, 0, 3, 0, 12, 0, 109 }
We can see each word will result in a 0, since 0 will be returned if the parsing failed - see: Int32.TryParse(...).
And therefore it is impossible to differentiate between an real 0 in your input or a word, which could not be parsed.
But we can tweek the convert a little bit by using Nullable<int>/int?
int?[] num = Array.ConvertAll(strArray1, x =>
{
return (int?)( int.TryParse(x, out int result) ? result : null);
});
// num: Nullable<int>[8] { null, 99, null, 3, null, 12, null, 109 }
So with this we are able to differ between a 0 and a word - represented by null.
Now you can perform other operations like num.Max() and num.Min().
If you need an int[] array, you have add some more function calls.
int[] num = Array.ConvertAll(strArray1, x =>
{
return (int?)( int.TryParse(x, out int result) ? result : null);
}).OfType<int>().ToArray();
//num: int[4] { 99, 3, 12, 109 }
So this is not quite optimal.
My solution for this would be a simple loop.
// Your intput
string[] strArray1 = { "Bautik", "99", "Sagar", "3", "Tisha", "12", "Riya", "109" };
// Output Variables
List<int> result = new();
int min = int.MaxValue;
int max = int.MinValue;
// Loop
foreach( string item in strArray1)
{
if(int.TryParse(item, out int value))
{
result.Add(value);
min = min > value ? value : min;
max = max < value ? value : max;
}
}
// Output
// result: List<int>[4] { 99, 3, 12, 109 }
// min: 3
// max: 109
As already mentioned by Gian Paolo in the comments of this answer
working with a List<int> is much easier than working with an int[]
..., but if you really need an array, then you can just call ToArray().
int[] resultArray = result.ToArray();
Which results in int[4] { 99, 3, 12, 109 }.

How to sort a jagged array [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I'm making a program to automate rolling initiative. I have most of it done, but I can't sort the outputs because it's a jagged array. I need the second column of each array in my jagged array to be sorted from highest to lowest.
using System;
using System.Linq;
namespace Auto_Initiative
{
class Program
{
static void Main(string[] args)
{
string[] encounter =
{
"Wizard", "18", "-2",
"Bard", "9", "3",
"Goblin 1", "16", "1",
"Goblin 2", "14", "1"
};
int[][] numbers = new int[encounter.Length / 3][];
int loop = 0;
for(int i = 0; i > encounter.Length; i += 3)
{
// Name number, real roll
numbers[loop] = new int[2] {i, Int32.Parse(encounter[i + 1]) + Int32.Parse(encounter[i + 2])};
}
Console.ReadKey();
}
}
}
One part of designing your software is choosing the right data structure for how you are planning to use it. Sometimes redundant data is required but we don't know what you are requirements are to make that decision. So as was mentioned by Sergey you should consider creating a custom class which I have shown an example of below. Also note that a string[] is not really a jagged array. By definition a jagged array has nested arrays of variable size. The data structure depicted above could be put in a regular string[][] and would not be jagged.
Object Oriented in Action
What you are looking for is stored in unitsSortedBySecondColumn.
class so65865986
{
static void Main(string[] args)
{
Encounter encounter = new Encounter
{
Units = new List<EncounterUnit> {
new EncounterUnit{
Name = "Wizard",
Column1 = 18,
Column2 = -2,
},
new EncounterUnit{
Name = "Bard",
Column1 = 9,
Column2 = 3,
},
new EncounterUnit{
Name = "Goblin 1",
Column1 = 16,
Column2 = 1,
},
new EncounterUnit{
Name = "Goblin 2",
Column1 = 14,
Column2 = 1,
},
},
};
var unitsSortedBySecondColumn = encounter.Units
.OrderBy(u => u.Column1)
.Select(u => new int[] { u.Column1, u.Column2 })
.ToArray();
}
}
class EncounterUnit
{
public string Name;
public int Column1; //Change name to whatever it means
public int Column2; //Change name to whatever it means
}
class Encounter
{
public List<EncounterUnit> Units;
}
Nested (but not Jagged) Array
class so65865986_nested_array
{
static void Main(string[] args)
{
string[][] encounter =
{
new string[] {"Wizard", "18", "-2" },
new string[] {"Bard", "9", "3" },
new string[] {"Goblin 1", "16", "1" },
new string[] {"Goblin 2", "14", "1" },
};
int[][] numbers = encounter
.Select(u => new int[] { int.Parse(u[1]), int.Parse(u[2]) })
.OrderBy(u => u[0])
.ToArray();
Console.ReadKey();
}
}
Other Notes
Also, another note. You don't need to use Int32 because it is recommended you use the aliases provided which in this case is int.
use this code:
string[] encounter =
{
"Wizard", "18", "-2",
"Bard", "9", "3",
"Goblin 1", "16", "1",
"Goblin 2", "14", "1"
};
int[,] numbers = new int[encounter.Length / 3, 3];
for (int i = 1; i < encounter.Length / 4; i++)
{
for (int j = 0; j < encounter.Length / 3; j += 1)
{
numbers[j, i] = Convert.ToInt32(encounter[i + (j * 3)]);
Console.Write(numbers[j, i] + " ");
}
Console.WriteLine(Environment.NewLine);
}
Console.ReadLine();

How to Define that One String is Greater than Another? (C#) (Beginner)

I'm extremely new to coding, so the answer/s to this may be obvious. I have to make the card game War. I've created a list of strings like so for a part of the deck:
List<string> userDeck = new List<string>
{
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"Jack",
"Queen",
"King",
"Ace",
};
Is there a way I can instruct the computer that "Jack" will be greater than "10", "Queen" greater than "Jack", and so on? I'm not sure where or how I should do it.
OR, if you have any other suggestions as to how I should do this, please let me know. I have to be using a list. I initially made a list of integers instead, but I wasn't aware of an uncomplicated way to assign the names such as "Jack," "Queen," "King," etc. to them for display purposes.
Any help would be greatly appreciated.
Try to create a object called "Card". This object can contain more than a value. Eg:
public class MyCard
{
public string Name {get;set;}
public int Value {get;set;}
public MyCard(string name, int value)
{
this.Name = name;
this.Value = value;
}
}
After create this object, you will use this at your list.
List<MyCard> userDeck = new List<MyCard>();
You can fill the list this way:
usertDeck.Add(new MyCard("2", 1));
....
usertDeck.Add(new MyCard("K", 11));
So, to compare 2 cards, just check using the "Value" variable
The easiest way to refactor your code (to your requirements value per card) is to use Tuple's instead of string type in generic List as follow:
List<Tuple<int,string>> userDeck = new List<Tuple<int,string>>
{
new Tuple<int,string>(2,"2"),
new Tuple<int,string>(3,"3"),
new Tuple<int,string>(4,"4"),
new Tuple<int,string>(5,"5"),
new Tuple<int,string>(6,"6"),
new Tuple<int,string>(7,"7"),
new Tuple<int,string>(8,"8"),
new Tuple<int,string>(9,"9"),
new Tuple<int,string>(10,"10"),
new Tuple<int,string>(11,"Jack"),
new Tuple<int,string>(12,"Queen"),
new Tuple<int,string>(13,"King"),
new Tuple<int,string>(14,"Ace"),
};
Similar to Vincius's answer but with some changes for better usability:
enum Suit
{
Clubs = 1,
Diamonds = 2,
Hearts = 3,
Spades = 4
}
class Card
{
private static readonly Dictionary<string, int> rankMap = new Dictionary<string, int>()
{
{"2", 2 },
{"3", 3 },
{"4", 4 },
{"5", 5 },
{"6", 6 },
{"7", 7 },
{"8", 8 },
{"9", 9 },
{"10", 10 },
{"Jack", 11 },
{"Queen", 12 },
{"King", 13 },
{"Ace", 14 },
};
private Suit suit;
private string rank;
public Suit Suit => suit;
public string Rank => rank;
public int Value { get { return rankMap[rank]; } }
public Card(Suit s, string r)
{
suit = s;
rank = r;
}
}
The way you can use it:
Card c1 = new Card(1, "Jack"); // 1 is Clubs
Card c2 = new Card(4, "Queen"); // 4 is Spades
Console.WriteLine(c1.Value); // prints 11
Console.WriteLine(c2.Value); // prints 12

IndexOutOfBounds error [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
So I'm new to programming. I have printed two arrays next to each other. On the left column, I have Ace,2,3,4.. up until Jack, Queen king. One the right column is my suites. So Clubs, hearts etc. Now I want to randomize my whole deck, but I find only for example 3 Hearts print, nothing else. Also, I get an IndexOutOfBounds error.I can't figure out why.
Here's my code:
string[] suites = new string[] { "hearts", "clubs", "diamonds", "spades" };
string [] pack = new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13" };
Random r = new Random();
string[] x = new string[] { suites[r.Next(0,3)].ToString() };
string[] y = new string[] { pack[r.Next(0,12)].ToString() };
for (int a = 0; a < 13; a++)
{
for (int p = 0; p < 4; p++)
{
Console.WriteLine(y[a] + " " + x[p]);
}
}
Replace below lines
string[] x = new string[] { suites[r.Next(0,3)].ToString() };
string[] y = new string[] { pack[r.Next(0,12)].ToString() };
With
string[] x = suites.OrderBy(a => r.Next()).ToArray();
string[] y = pack.OrderBy(a => r.Next()).ToArray();

C# create list with multiple entries

How can I create a list with a fixed set of entries.
Learning C# and doing an exercise to list all cards in a deck of cards(without jokers). Going to use two foreach loops to print them out.
However I cannot get a default list of the cards(I am overloading the method). Looking at the docs http://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx and some examples http://www.dotnetperls.com/list and each element is added in individually.
// from dotnet perls
class Program
{
static void Main()
{
List<string> dogs = new List<string>(); // Example List
dogs.Add("spaniel"); // Contains: spaniel
dogs.Add("beagle"); // Contains: spaniel, beagle
dogs.Insert(1, "dalmatian"); // Contains: spaniel, dalmatian, beagle
foreach (string dog in dogs) // Display for verification
{
Console.WriteLine(dog);
}
}
}
I have tried both the Add and Insert methods but cannot create my lists.
// my code
using System;
using System.Collections.Generic;
using System.Linq;
class Deck
{
static void Main()
{
List<string> deck = new List<string>();
deck.Insert("Ace", 2, 3, 4, 5, 6, 7, 8, 9, 10, "Jack", "Queen", "King");
List<string> colour = new List<string>();
colour.Add("Hearts", "Diamonds", "Spades", "Clubs");
foreach (string card in deck)
{
foreach(string suit in colour)
{
Console.Write(colour + " " + card);
}
}
}
}
List.Add or List.Insert doesn't take variable length parameters. You may need to use List.AddRange method.
List<string> deck = new List<string>();
deck.AddRange(new []{"Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"});
List<string> colour = new List<string>();
colour.AddRange(new []{"Hearts", "Diamonds", "Spades", "Clubs"});
Also note that I've converted the numerical parameters to string as the list is List<string>, otherwise it won't compile.

Categories

Resources