How to exclude unwanted matches from randomly matched strings - c#

For example I have such a code.
string[] person = new string[] { "Son", "Father", "Grandpa" };
string[] age = new string[] { "Young", "In his 40-s", "Old" };
string[] unwanted = new string { "Old Son", "Young GrandPa" };
Random X = new Random();
string Who = person[i.Next(0, person.Length)];
string HowOld = age[i.Next(0, age.Length)];
Console.WriteLine("{0} {1}", Who, HowOld);
I want to get all random matches BUT THEN exclude two variants from the array "unwanted"). (surely it's just an example, there can be many more arrays and possible bad matches).
What is the good way to do it? The keypoint that I wanna keep the possibility to get ALL results anyway. So I wanna have option to exclude stuff AFTER generation, but not making it impossible to generate "old son".

First, define a class that holds both values from the arrays:
class PersonWithAge
{
public string Person { get; set; }
public string Age { get; set; }
}
Next, use LINQ to generate all possible combinations of Person and Age:
// Create cross product
var results = (from x in person
from y in age
select new PersonWithAge{Person=x, Age=y}).ToList();
Now (if desired) remove the exceptions:
results.RemoveAll(n => n.Person == "Son" && n.Age == "Old"
|| n.Person == "Grandpa" && n.Age == "Young");

If you want to prevent some combination I belief you could have 'rules' of pairs/groups that cannot be matched together like for instance an string[][] blocked or int[][] blocked when, if accessing blocked[i][j], i is the current word and the array blocked[i] are the indexes of the words (or the words themselves) it cannot be matched with (all of this assuming you might have more than 1 word you potentially don't want to match to the current one, in case of just one a simple array will suffice), Then you just have to make sure the random value you use is not one of those 'forbidden'. Hope this helps

Related

Sorting a list (string) numerically from high to low c#

Context: I am designing a leader board and want to have the data displayed from the highest score to the lowest score. The problem being the data contains a string and integers which are imported from a text file. Currently I am able to sort the data numerically however I am using the OrderByDescending function which does not work. E.g 11,4,5,8,23,65 when ordered = 8,65,5,4,23,11 (sorted alphanumerically).
The list contains the data: name(string), difficulty(string) and score(int) and I wish to sort the data in a descending order: E.g 1st = 10, 2nd = 9 etc.
List<string> leaderboardList = new List<string>();
StreamReader srUserData = new StreamReader(#"User Leaderboard.txt");
while ((userDataLine = srUserData.ReadLine()) != null)
{
leaderboardList.Add(userDataLine);
}
leaderboardList = leaderboardList.OrderByDescending(x => Regex.Match(x, #"\d+").Value).ToList();
The Regex.Match finds the number in the string.
Basically the final line is the line that needs amending.
All help welcome, thanks.
Edit: The data should be outputted in the form Name, difficulty, score and sort the data in a descending order with the highest score.
Just change:
leaderboardList = leaderboardList.OrderByDescending(x =>
Regex.Match(x, #"\d+").Value).ToList();
To:
leaderboardList = leaderboardList.OrderByDescending(x =>
Int32.Parse(Regex.Match(x, #"\d+").Value)).ToList();
Just add the Int32.Parse really. A caveat though: If you pass a string that is not a number Int32.Parse() will throw an exception. If this is a possibility that needs to be handled, then you can use Int32.TryParse instead.
Ex:
int testValue = 0; //This is only used for TryParse()
leaderboardList = leaderboardList.OrderByDescending(x =>
Int32.TryParse(Regex.Match(x, #"\d+").Value, out testValue)
? testValue : 0M).ToList();
Tested using the following example list:
List<string> leaderboardList = new List<string>();
leaderboardList.Add("Brandon|Easy|9");
leaderboardList.Add("Yoda|Impossible|9001");
leaderboardList.Add("Barney|Easy|-1");
leaderboardList.Add("John|Normal|500");
This code works correctly, and the TryParse code was not needed.
Output:
Yoda|Impossible|9001
John|Normal|500
Brandon|Easy|9
Barney|Easy|-1
I suggest a completely different approach from the others. Load your string from disk, then parse it into a class you have defined. For example:
public class LeaderboardRow
{
public string Name { get; set; }
public string Difficulty { get; set; }
public int Score { get; set; }
}
Then your code would look more like this:
List<LeaderboardRow> leaderboardList = new List<LeaderboardRow>();
StreamReader srUserData = new StreamReader(#"User Leaderboard.txt");
while ((userDataLine = srUserData.ReadLine()) != null)
{
//Put logic here that parses your string row into 3 distinct values
leaderboardList.Add(new LeaderboardRow()
{
Score = 0, //put real value here
Name = string.Empty, //put real value here
Difficulty = string.Empty //put real value here
});
}
Then any ordering you need to do is a simple LINQ statement:
leaderboardList = leaderboardList.OrderByDescending(x => x.Score).ToList();
Depending on your scenario/requirements you could store this text as json instead which could speed up and simplify your application.
You could go old school and perform Integer.TryParse or some derivative of such functions. Once you do that you can append them into a list of integers.
int number;
bool result = Int32.TryParse(value, out number);
if (result) {
Console.WriteLine("Converted '{0}' to {1}.", value, number);
}
You can use this for ordering list of numbers in string or list of string that contains numbers.
The Alphanum Algorithm
I hope this works for you.
You missed one thing, that is you are not convert Regex value to int. Please check below code, it's works for me.
CODE:
List<string> leaderboardList = new List<string>();
leaderboardList.Add("A,A,9");
leaderboardList.Add("B,B,8");
leaderboardList.Add("G,C,65");
leaderboardList.Add("S,B,10");
leaderboardList = leaderboardList.OrderByDescending(x =>Convert.ToInt32((string.IsNullOrEmpty(Regex.Match(x, #"\d+").Value)?"0":Regex.Match(x, #"\d+").Value))).ToList();
foreach (var item in leaderboardList)
{
Console.WriteLine(item);
}
Online output: .Net Fiddle Output

match multiple terms in a string to determine topic

I am trying to search a user-entered string against a list of known terms to determine a topic. That is, I maintain my own list of topics and related keywords, and want to match against the user-entered string to determine the topic(s) it relates to. However, I want to make sure multiple terms are "hit" to avoid false-positives.
e.g. based on the code:
//create a list of topic keywords
List<string> CivilWar = new List<string>()
{
"Confederacy", "Union", "Civil War", "Lincoln", "Stonewall Jackson"
};
//does the user agent string exist in the list?
bool isTopic = CivilWar.Exists(x => source.Contains(x));
return isTopic
the string "Stonewall Jackson fought for the Confederacy" returns a correct positive / true result, but the string "John Kennedy Toole wrote A Confederacy of Dunces" returns a false positive / true result.
How can I make sure multiple terms are required to score a positive?
bool isTopic = CivilWar.Where(x => source.Contains(x)).Count() > 1;
Use Count instead of Exists, and make sure it is greater than 1 (multi-term):
//create a list of topic keywords
List<string> CivilWar = new List<string>()
{
"Confederacy", "Union", "Civil War", "Lincoln", "Stonewall Jackson"
};
//does the user agent string exist in the list?
return CivilWar.Count(x => source.Contains(x)) > 1; //must be greater than 1

C# Split text array into sub array

I can't seem to find much on this online :(
For my course I have to get text from a grocery.txt file and output an invoice.
The text file looks like this:
regular,bread,2.00,2
regular,milk,2.00,3
regular,cereal,4.00,1
fresh,rump steak,11.99,0.8
fresh,apple,4.60,1.00
fresh,cucumber,2.20,0.936
regular,cream,2.20,1
regular,mustard,3.30,1
fresh,carrots,1.79,1.5
regular,tomato sauce,2.95,1
regular,salt,2.80,1
fresh,bananas,4.00,1.2
Currently I use this method to get the text:
string[] groceryItemsArray = System.IO.File.ReadAllLines(#"C:\C\groceries.txt");
What I get is an array that in stores the entire line (for example "fresh,bananas,4.00,1.2").
I have no idea how to split the array into sub arrays or even whats the best way to go about this. Unfortunately this course task are way too advanced for the little material we have been taught and time we have for this. I have already spend around 6 hours watching videos and getting not very far in learning the advanced stuff.
This is what I am trying to accomplish.
A. Class named grocerrItem. This class has properties:
name and price
this class must have 2 constructors
A. subclass of this is a purchasedItem.
this class has an integer property 'quantity'
one of its methods findCost, calculates the cost of the item, including 10% GST.
the formula for this is price * quantity * 1.1
C. A subclass of this class (purchasedItem), freshItem has
a property weight, a double type.
The findCost method here, uses the formula:wieght * price, as it is not subject to GST.
Groceries.txt contains information as follows:
type(regular or fresh), name, price, quantity/weight - depending on being regular or fresh.
** An invoice should be represented as a collection of grocery items (parent class). This can be done using any container structure i.e. array, list or collection. you will then use iteration
You can just use String.Split which returns you the sub array that you want.
public static void Main()
{
string[] groceryItemsArray = System.IO.File.ReadAllLines(#"C:\C\groceries.txt");
// now split each line content with comma. It'll return you an array (sub-array)
foreach (var line in groceryItemsArray)
{
string[] itemsInLine = line.Split(',');
// Do whatevery you want to do with this.
// e.g. for 1st line itemsInLine array will contains 4 items
// "regular","bread","2.00", "2"
}
}
You can use this code (I'm not sure what exactly you looking for):
/// It's return all text as a single list
var groceryItemsArray = System.IO.File.ReadAllLines(#"C:\C\groceries.txt")
.SelectMany(x => x.Split(new char[]{','} , StringSplitOptions.RemoveEmptyEntries)).ToList() ;
Or if want to return as Child List can use this code:
var groceryItemsArray = System.IO.File.ReadAllLines(#"C:\C\groceries.txt").Select(x =>
new { Child = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) }).ToList();
I didn't really understand what are you trying to output, but here's some basic things I did understand on your question:
(code not tested)
I created a parent class:
class GroceryItem
{
public string ItemName {get;set;}
public decimal Price {get;set;}
public GroceryItem() { }
}
And creates a child class:
class PurchasedItem : GroceryItem
{
public int Quantity { get;set; }
public decimal Cost { get;set; }
public PurchasedItem(string[] item)
{
ItemName = item[1];
Price = decimal.Parse(item[2]);
Quantity = int.Parse(item[3]);
FindCost();
}
private void FindCost()
{
Cost = Price * Quantity * 1.1;
}
}
Using your input to get all groceryItems, you can iterate using foreach loop and collect a list of purchasedItems.
Main()
{
string[] groceryItems = System.IO.File.ReadAllLines(#"C:\C\groceries.txt");
var purchasedItems = new List<PurchasedItem>();
foreach (var item in groceryItems)
{
string[] line = item.Split(',');
purchasedItems.Add(new PurchasedItem(line));
}
}
Well if you didn't understand this basic concept of programming, you better start on this link.

Custom List<string[]> Sort

I have a list of string[].
List<string[]> cardDataBase;
I need to sort that list by each list-item's second string value (item[1]) in custom order.
The custom order is a bit complicated, order by those starting characters:
"MW1"
"FW"
"DN"
"MWSTX1CK"
"MWSTX2FF"
then order by these letters following above starting letters:
"A"
"Q"
"J"
"C"
"E"
"I"
"A"
and then by the numbers following above.
a sample, unordered list left, ordered right:
MW1E10 MW1Q04
MWSTX2FFI06 MW1Q05
FWQ02 MW1E10
MW1Q04 MW1I06
MW1Q05 FWQ02
FWI01 FWI01
MWSTX2FFA01 DNC03
DNC03 MWSTX1CKC02
MWSTX1CKC02 MWSTX2FFI03
MWSTX2FFI03 MWSTX2FFI06
MW1I06 MWSTX2FFA01
I tried Linq but I am not that good in it right now and cannot solve this on my own. Do I need a dictionary, regex or a dictionary with regex in it? What would be the best approach?
I think you're approaching this incorrectly. You're not sorting strings, you're sorting structured objects that are misrepresented as strings (somebody aptly named this antipattern "stringly typed"). Your requirements show that you know this structure, yet it's not represented in the datastructure List<string[]>, and that's making your life hard. You should parse that structure into a real type (struct or class), and then sort that.
enum PrefixCode { MW1, FW, DN, MWSTX1CK, MWSTX2FF, }
enum TheseLetters { Q, J, C, E, I, A, }
struct CardRecord : IComparable<CardRecord> {
public readonly PrefixCode Code;
public readonly TheseLetters Letter;
public readonly uint Number;
public CardRecord(string input) {
Code = ParseEnum<PrefixCode>(ref input);
Letter = ParseEnum<TheseLetters>(ref input);
Number = uint.Parse(input);
}
static T ParseEnum<T>(ref string input) { //assumes non-overlapping prefixes
foreach(T val in Enum.GetValues(typeof(T))) {
if(input.StartsWith(val.ToString())) {
input = input.Substring(val.ToString().Length);
return val;
}
}
throw new InvalidOperationException("Failed to parse: "+input);
}
public int CompareTo(CardRecord other) {
var codeCmp = Code.CompareTo(other.Code);
if (codeCmp!=0) return codeCmp;
var letterCmp = Letter.CompareTo(other.Letter);
if (letterCmp!=0) return letterCmp;
return Number.CompareTo(other.Number);
}
public override string ToString() {
return Code.ToString() + Letter + Number.ToString("00");
}
}
A program using the above to process your example might then be:
static class Program {
static void Main() {
var inputStrings = new []{ "MW1E10", "MWSTX2FFI06", "FWQ02", "MW1Q04", "MW1Q05",
"FWI01", "MWSTX2FFA01", "DNC03", "MWSTX1CKC02", "MWSTX2FFI03", "MW1I06" };
var outputStrings = inputStrings
.Select(s => new CardRecord(s))
.OrderBy(c => c)
.Select(c => c.ToString());
Console.WriteLine(string.Join("\n", outputStrings));
}
}
This generates the same ordering as in your example. In real code, I'd recommend you name the types according to what they represent, and not, for example, TheseLetters.
This solution - with a real parse step - is superior because it's almost certain that you'll want to do more with this data at some point, and this allows you to actually access the components of the data easily. Furthermore, it's comprehensible to a future maintainer since the reason behind the ordering is somewhat clear. By contrast, if you chose to do complex string-based processing it's often very hard to understand what's going on (especially if it's part of a larger program, and not a tiny example as here).
Making new types is cheap. If your method's return value doesn't quite "fit" in an existing type, just make a new one, even if that means 1000's of types.
A bit spoonfeeding, but I found this question pretty interesting and perhaps it will be useful for others, also added some comments to explain:
void Main()
{
var cardDatabase = new List<string>{
"MW1E10",
"MWSTX2FFI06",
"FWQ02",
"MW1Q04",
"MW1Q05",
"FWI01",
"MWSTX2FFA01",
"DNC03",
"MWSTX1CKC02",
"MWSTX2FFI03",
"MW1I06",
};
var orderTable = new List<string>[]{
new List<string>
{
"MW1",
"FW",
"DN",
"MWSTX1CK",
"MWSTX2FF"
},
new List<string>
{
"Q",
"J",
"C",
"E",
"I",
"A"
}
};
var test = cardDatabase.Select(input => {
var r = Regex.Match(input, "^(MW1|FW|DN|MWSTX1CK|MWSTX2FF)(A|Q|J|C|E|I|A)([0-9]+)$");
if(!r.Success) throw new Exception("Invalid data!");
// for each input string,
// we are going to split it into "substrings",
// eg: MWSTX1CKC02 will be
// [MWSTX1CK, C, 02]
// after that, we use IndexOf on each component
// to calculate "real" order,
// note that thirdComponent(aka number component)
// does not need IndexOf because it is already representing the real order,
// we still want to convert string to integer though, because we don't like
// "string ordering" for numbers.
return new
{
input = input,
firstComponent = orderTable[0].IndexOf(r.Groups[1].Value),
secondComponent = orderTable[1].IndexOf(r.Groups[2].Value),
thirdComponent = int.Parse(r.Groups[3].Value)
};
// and after it's done,
// we start using LINQ OrderBy and ThenBy functions
// to have our custom sorting.
})
.OrderBy(calculatedInput => calculatedInput.firstComponent)
.ThenBy(calculatedInput => calculatedInput.secondComponent)
.ThenBy(calculatedInput => calculatedInput.thirdComponent)
.Select(calculatedInput => calculatedInput.input)
.ToList();
Console.WriteLine(test);
}
You can use the Array.Sort() method. Where your first parameter is the string[] you're sorting and the second parameter contains the complicated logic of determining the order.
You can use the IEnumerable.OrderBy method provided by the System.Linq namespace.

Naming a variable from a text file

I'm making a program in C# that uses mathematical sets of numbers. I've defined the class Conjunto (which means "set" in spanish). Conjunto has an ArrayList that contains all the numbers of the set. It also has a string called "ID" which is pretty much what it sounds; the name of an instance of Conjunto.
The program have methods that applies the operations of union, intersection, etc, between the sets.
Everything was fine, but now i've a text file with sentences like:
A={1,2,3}
B={2,4,5}
A intersection B
B union A
And so on. The thing is, i don't know how many sets the text file contains, and i don't know how to name the variables after those sentences. For example, name an instance of Conjunto A, and name another instance B.
Sorry for the grammar, english is not my native language.
Thanks!
It's pretty complicated to create varaibles dynamically, and pretty useless unless you have some already existing code that expects certain variables.
Use a Dictionary<string, Conjunto> to hold your instances of the class. That way you can access them by name.
First off, If you don't target lower version than .Net 2.0 use List instead of ArrayList. If I were you I wouldn't reinvent the wheel. Use HashSet or SortedSet to store the numbers and then you can use defined union and intersection.
Secondly, what is your goal? Do want to have just the output set after all operations? Do you want to read and store all actions and them process it on some event?
First of all, your program is taken from bad side. I would advice to start making new one. One of ways to name "variables" dynamicaly is by making class objects and editing their properties.
This is what I made as a starting platform:
First af all I have crated a class called set
class set
{
public string ID { get; set; }
public List<int> numbers { get; set; }
}
Then I have made the code to sort whole textfile into list of those classes:
List<set> Sets = new List<set>();
string textfile = "your text file";
char[] spliter = new char[] { ',' }; //switch that , to whatever you want but this will split whole textfile into fragments of sets
List<string> files = textfile.Split(spliter).ToList<string>();
int i = 1;
foreach (string file in files)
{
set set = new set();
set.ID = i.ToString();
char[] secondspliter = new char[] { ',' }; //switch that , to whatever you want but this will split one set into lone numbers
List<string> data = textfile.Split(secondspliter).ToList<string>();
foreach (string number in data)
{
bool success = Int32.TryParse(number, out int outcome);
if (success)
{
set.numbers.Add(outcome);
}
}
i++;
Sets.Add(set);
}
Hope it helps someone.

Categories

Resources