C# - find the position of a string inside a predefined string array - c#

I have a predefined string array with the letters from A to Q :
string[] SkippedAreasArray = new string[] {"A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q"};
In a TextBox inside Windows Form the user can enter skippedAreas like this: A,B,C,D... there is validations and restrictions to use only letters and commas so the input is guaranteed to be in this format.
What I do is taking the user input and populate another string array:
string[] SkippedAreasFromForm = new string[17];
...
SkippedAreasFromForm = (txtSkippedAreas.Text).Split(',');
Now comes the tricky part for which I'm seeking assistance. The user must enter Number of areas for example - 3. Which mean he is working only with A, B, and C. If the number of areas was 2 then he can only use A and B if the number of areas was 4 then A, B, C and D are available and so on.
What I need is to check if in the array SkippedAreasFromForm which is populated with the user input there is an area that doesn't match the above criteria.
What this mean in terms of coding - I need to take every element from SkippedAreasFromForm, take it's integer value from the predefined SkippedAreasArray and see if this value is equal or greater (>=) of the value that he entered as `Number of areas. If there is an area which is outside the scope of the selected number an error should be shown.
What I have right now is :
foreach (string tempAreaValue in SkippedAreasFromForm)
{
for (int i = 0; i < SkippedAreasArray.Length; i++)
{
if (tempAreaValue == SkippedAreasArray[i])
{
if ((i + 1) > entity.AreasCnt)
{
MessageBox.Show("You must use areas only within the Number of Areas scope!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
txtSkippedAreas.Focus();
return false;
}
}
}
}
For the few test I made it works. But first - at least for me it seems over complicated. Second - I'm not sure that this algorithm is working exactly as I need to or I'm just having correct results by luck. Third - I'm coding C# for 2months now and this seems to me like an excellent candidate for a LINQ expression - do you think it would be better using LINQ and I would appreciate a help in the transformation.

I think you're just looking for IndexOf:
int index = SkippedAreasArray.IndexOf(tempAreaValue);
if (index >= entity.AreasCnt)
{
...
}
(You might also want to check for index being -1, which would occur if the element wasn't in the list at all. Also, consider duplicates - can the user enter A, A, A?)

If you are looking for the elements in the array from start index to max string elements based on areaNumber, then:
int areaNumber = 4;
var result = SkippedAreasArray.Select((r, i) => new { Value = r, Index = i })
.Where(r => r.Index <= areaNumber - 1)
.Select(r => r.Value)
.ToArray();
For areaNumber 4 you will get "A", "B", "C", "D"
EDIT:
I'm looking if every element from the user input (which is array
A,C,H,Q..) is inside the area determined from the areNumber (from comment)
Suppose your userInputArray is:
string[] userInputArray = new string[] {"A", "C", "H", "Q"};
Then you can use Enumerable.Except in the following manner:
if(result.Except(userInputArray).Any())
{
//Invalid
}
else
{
//Valid
}

Related

Loop Count String (ex A to ABC) C# [duplicate]

This question already has answers here:
Iterating through the Alphabet - C# a-caz
(10 answers)
Closed 6 years ago.
I am not sure how to search for this, or I would. What I need to do is count up with letters rather than numbers. the input will only contain letters and numbers, no spaces, or dashes.
For example, if the user enters "A" for start and "ABC" for end it would output, A, B, C,...AA, AB, AC,...ABC.
I can do it by breaking everything down to an array, increase, the last index, until "Z", then, increase the index, check if it is equals end, then loop again.When "Z" is hit and it has a number (treated as a string) start to loop through "0 - 9".
It just seems there would be an easier way, than what I am thinking. I seen one solution in Java that I could convert, but not fully understanding how it works SO Post it is using MOD and does not really compare values. This is just a small project for a computer lab, to generate NetBIOS names, for another program to use. All the names are sequential.
Thanks,
Dave
Dim array1 As String() = {"a", "b", "c"}
Dim array2 As String() = {"a", "b", "c"}
Dim result = array1.SelectMany(Function(f) array2, Function(f, a) New With {Key .first = f, Key .second = a})
Dim s As String = String.Empty
For i As Integer = 0 To result.Count - 1
s += result(i).first + result(i).second + " "
Next
MessageBox.Show(s)
output:
aa
ab
ac
ba
bb
bc
ca
cb
cc
I think this may be close to what you are after. Similar to a cross-join in SQL. This works in VB.Net but you can run a code converter
EDIT: (Ran through code converter (vb to c#), untested)
string[] array1 = {
"a",
"b",
"c"
};
string[] array2 = {
"a",
"b",
"c"
};
dynamic result = array1.SelectMany(f => array2, (f, a) => new {
first = f,
second = a
});
string s = string.Empty;
for (int i = 0; i <= result.Count - 1; i++) {
s += result(i).first + result(i).second + " ";
}
MessageBox.Show(s);
//=======================================================
//Service provided by Telerik (www.telerik.com)
//Conversion powered by NRefactory.
//Twitter: #telerik
//Facebook: facebook.com/telerik
//=======================================================

Generating Base 64 IDs

This question will be mainly opinion based I think, however, I have created my own Base64 ID generator for the visible ID column of my database tables for security reasons, (I saw a video as to why YouTube do it and it made sense although I see other ways of security that probably aren't as problematic). It handles the HIGHLY unlikely event that a duplicate may arise, however, I am curious to know if this is used as a standard for say, YouTube's video IDs.
Program.cs
class Program
{
static void Main(string[] args)
{
var ids = new HashSet<string>();
var count = 0; // for testing only
while (count < 8)
{
ids.Add(Base64Id.GenerateId(ids));
Console.ReadLine();
count++; // for testing only
}
}
}
Base64Id.cs
public static class Base64Id
{
private static int IdSize = 1; // Should be 11
private static readonly string[] AllowedChars = {
"0", "1", "2", "3", "4", "5", "6", "7"//,
//"8", "9", "a", "b", "c", "d", "e", "f",
//"g", "h", "i", "j", "k", "l", "m", "n",
//"o", "p", "q", "r", "s", "t", "u", "v",
//"w", "x", "y", "z", "A", "B", "C", "D",
//"E", "F", "G", "H", "I", "J", "K", "L",
//"M", "N", "O", "P", "Q", "R", "S", "T",
//"U", "V", "W", "X", "Y", "Z", "-", "_"
};
private static Random _random = new Random();
/// <summary>
/// To generate a Base64 ID and check to make sure the ID is not already in use.
/// </summary>
/// <param name="usedIds">List of IDs already in use from the Database or other source.</param>
/// <returns>New Base64 ID</returns>
public static string GenerateId(HashSet<string> usedIds)
{
var autoGenId = "";
do
{
autoGenId = "";
for (var i = 0; i < IdSize; i++)
autoGenId += GetRandomChar();
#if DEBUG
_DEBUG_(usedIds.Count() + 1, autoGenId);
#endif
}
while (IsTaken(autoGenId, usedIds));
return autoGenId;
}
private static string GetRandomChar()
{
var i = _random.Next(0, AllowedChars.Length);
return AllowedChars[i];
}
private static bool IsTaken(string id, HashSet<string> usedIds)
{
var check = usedIds.Any(i => id.Contains(i));
if (check)
return true;
return false;
}
private static void _DEBUG_(int count, string id)
{
Console.WriteLine(String.Format("{0}:\t{1}", count, id));
}
}
I believe that this will work like a charm for my purposes and without any issues, as it does during testing. However, once I narrow it down to 8 chars and with an ID size of length 1, it kicks up serious errors due to the constant looping after only 6 of the 8 expected outputs have happened.
I understand this is from a random number being hit each time and the less that there are to choose from, the more likely the loop must occur.
I know ways to remedy this, however on the scale I plan for, it is crazy to think of, such as having an array/list of every possibility and removing the selected ID.
This is my question;
Do the likes of Youtube know of this problem, and just don't care due to the size of possible IDs.
They just have much greater thought put into the class.
They do not care about the processing cost for such high numbers and handle every small detail.
Or do they use Base64Encode in there code instead of auto-generating it.
I would like to know your opinions and suggestions on how to improve the code or if it even matters which such huge numbers. I have answered with what I think could be possible ways to improve it.
UPDATE
I left two consoles run over the weekend, one using List and the other using HashSet, and the difference between the processed records is on a whole other level. So I have changed the above code to have HashSet instead of List, and set the code up to run automatically.
I believe that for the amount of possible IDs, it is not worth the serious amount of processing it would require to make sure that the ID is unique the further it goes to the end of the list as there are 73,786,976,294,838,206,464 possibilities.
Say for ten possible IDs 1 -> 10, if 2 have been selected already, then the next time, it has a 20% chance of be duplicated, and if 8 have been selected it will be 80% chance, each time. Using probability, this will stack and lower your chances of getting a unique ID to appear.
This is quiet bad once the numbers are low, it took 14539279 iterations the first time and 662984 iterations the second time to get all 8 unique IDs to appear. With bigger numbers I know it will take longer to get to this breaking point, but it will be much worse.
I think that this could be split up into a binary tree once the numbers get bigger as to make the most of it, once each block of say a couple 100k or million reaches 50% or 60% usage, forget the rest and move onto the next range.
I think this could be a good way to try and speed up processing a unique ID into the later stages of the possible list.

Array.BinarySearch() and missing letters

I've just a small question about Array.BinarySearch() and about letters what this method somehow doesn't see or cannot recognize.
I created a basic array to try out this method what I've just come across:
string[] Letters1 = { "q", "j", "i", "o" };
int index1 = Array.BinarySearch(Letters1, "q");
Console.WriteLine(index1);
Console.ReadKey();
string[] Letters2 = { "i", "q", "o", "a" };
int index2 = Array.BinarySearch(Letters2, "q");
Console.WriteLine(index2);
Console.ReadKey();
Somehow when start changing the letters and their position it starts giving wrong results
like in the case above...when I have q in first array in the first position and the resultis -5 and in the next array it's displayed correctly.
Binary search only works on ordered collections.
Array.BinarySearch : Searches a one-dimensional sorted Array for a value, using a binary search algorithm.

Permutation of a list of strings algorithm

I need help understanding how to write a permutation algorithm. (if this is even permutation, they have to be in order and use the same values).
List<string> str = new List<string>{"a", "b", "c", "d"};
How can I get a list of each permutation available in this list? For eg.
a, b, c, d
ab, c, d
ab, cd
abc, d
abcd
a, bc, d
a, bcd
a, b, cd
For some reason I cant find a pattern to start with. I'd also like to be able to disregard permutation when a joined string has a count of like X characters. So if X was 4, in that list, number 5 wouldn't exist and there would be 7 permutations.
private List<string> permute(List<string> values, int maxPermutation)
{
//alittle help on starting it would be great :)
}
I looked and read this, but he does not keep the order.
This is rather straightforward: you have three spots where you could either put a comma or to put nothing. There are eight combinations corresponding to 2^3 binary numbers.
For each number from 0 to 7, inclusive, produce a binary representation. Put a comma in each position where binary representation has 1; do not put comma where there's zero.
for (int m = 0 ; m != 8 ; m++) {
string s = "a";
if ((m & 1) != 0) s += ",";
s += "b";
if ((m & 2) != 0) s += ",";
s += "c";
if ((m & 4) != 0) s += ",";
s += "d";
Console.WriteLine(s);
}
You could take a recursive approach: Take the first letter, build all possible combinations starting with the second one (this is the recursion...) and prepend the first letter to each of them. Then take the first two letters together, recursively build all combinations starting with the third one. And so on ...
As for you additional requirement: If you want to exclude all "combinations" containing a string with X letters, just skip this number when constructing the first string.
The Binary approach above is correct and this is actually a partitioning problem (but not "The Partitioning Problem") and not a permutation problem.
http://en.wikipedia.org/wiki/Partition_of_a_set
Watch out because of the number of partitions grows faster than exponentially (e^e^n) so it will be really slow for large strings.
Try the following code. I haven't tested it, but I think it's what you are looking for.
List<string> str = new List<string>{ "a", "h", "q", "z", "b", "d" };
List<List<string>> combinations = combine(str.OrderBy(s=>s).ToList());
List<List<string>> combine(List<string> items)
{
List<List<string>> all = new List<List<string>>();
// For each index item in the items array
for(int i = 0; i < items.Length; i++)
{
// Create a new list of string
List<string> newEntry = new List<string>();
// Take first i items
newEntry.AddRange(items.Take(i));
// Concatenate the remaining items
newEntry.Add(String.concat(items.Skip(i)));
// Add these items to the main list
all.Add(newEntry);
// If this is not the last string in the list
if(i + 1 < items.Length)
{
// Process sub-strings
all.AddRange(combine(items.Skip(i + 1).ToList()));
}
}
return all;
}
If you need to generate combinations (or permutations or variations), then Combinatorics is a fantastic library.

How can I create an unique random sequence of characters in C#?

I'm implementing a URL shortening feature in my application in order to provide my users shorter alternative URLs that can be used in Twitter. The point is to be independent from the shortening services that offer this same service and include it as a feature of my web app.
What's the best way to create an unique random sequence of characters of about 6 chars? I plan to use that as an index for the items in my database that will have the alternative URLs.
Edited:
This feature will be used in a job board website, where every new job ad will get a custom URL with the title plus the shorter one to be used in Twitter. That said, the total number of unique 6 char combinations will be more than enough for a long time.
Do you really need 'random', or would 'unique' be sufficient?
Unique is extremely simple - just insert the URL into a database, and convert the sequential id for that record to a base-n number which is represented by your chosen characterset.
For example, if you want to only use [A-Z] in your sequence, you convert the id of the record to a base 26 number, where A=1, B=2,... Z=26. The algothithm is a recursive div26/mod26, where the quotient is the required character and the remainder is used to calculate the next character.
Then when retrieving URL, you perform the inverse function, which is to convert the base-26 number back to decimal. Perform SELECT URL WHERE ID = decimal, and you're done!
EDIT:
private string alphabet = "abcdefghijklmnopqrstuvwxyz";
// or whatever you want. Include more characters
// for more combinations and shorter URLs
public string Encode(int databaseId)
{
string encodedValue = String.Empty;
while (databaseId > encodingBase)
{
int remainder;
encodedValue += alphabet[Math.DivRem(databaseId, alphabet.Length,
out remainder)-1].ToString();
databaseId = remainder;
}
return encodedValue;
}
public int Decode(string code)
{
int returnValue;
for (int thisPosition = 0; thisPosition < code.Length; thisPosition++)
{
char thisCharacter = code[thisPosition];
returnValue += alphabet.IndexOf(thisCharacter) *
Math.Pow(alphabet.Length, code.Length - thisPosition - 1);
}
return returnValue;
}
The simplest way to make unique sequences is to do this sequentially, ie: aaaaaa aaaaab aaaaac ... These aren't necessarily the prettiest, but will guarantee uniqueness for the first 12230590463 sequences (provided you used a-z and A-Z as unique characters). If you need more URLs than that, you'd need to add a seventh char.
They aren't random sequences, though. If you make random ones, just pick a random char of the 48, 6 times. You'll need to check your existing DB for "used" sequences, though, as you'll be more likely to get collisions.
I would use an autonumber system, and create an algorithm to generate the keys. ie 1 = a, 2 = b, 27 = aa etc.
You can use the database autonumber to guarantee that your URL is unique, and you can calculate the URL possibly in a sproc in the DB or in your business layer?
Additionally you can now index on the incrementing number which is cheap and DB's are optimised for these to be used and hashed as primary/foreign keys as opposed to a variable length random string.
The usefulness of a random generator is limited to preventing users from plugging random URLs in to find things they shouldn't have a link to. If this is not your goal then sequential IDs should work just fine. If you just don't want to give users the impression that they are using "infant" technology (when they see that their job ad is #000001), why not start the sequence at some arbitrary value?
When you state "total number of unique 6 char combinations will be more than enough for a long time" for your random generation have you factored the birthday paradox into your calculations? This is generally the bane of any attempt to create random IDs within a range that is only 1 order of magnitude or less then the expected range that will be needed.
To create truly random IDs, you would need to create a loop that generates a new random value, checks to see if that value has already been used, and then repeats the loop if needed. The birthday paradox means that you quickly get to the point where many of the values generated are already in use (despite only a fraction of the total range being consumed), which causes the program to get slower and slower over time until it is taking thousands of attempts (and database lookups) to generate each ID.
I would suggest you go with the idea of encoding sequential IDs. To avoid the problem of users being able to simply increment/decrement the value in the URL to "explore", you can use a combination bit shifting and an alternate ordered list of letters (instead of 1=a, 2=b use 1=t, 2=j, etc).
Thinking about this more here is an idea.
You can start with a key table, incrementing chars AAAAAA - ZZZZZZ.
Then do a random select from that table each time you insert a new URL, and delete from the available keys.
Thoughts?
For random select try this link
Select a random row with MySQL:
SELECT column FROM table
ORDER BY RAND()
LIMIT 1
Select a random row with PostgreSQL:
SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1
Select a random row with Microsoft SQL Server:
SELECT TOP 1 column FROM table
ORDER BY NEWID()
Select a random row with IBM DB2
SELECT column, RAND() as IDX
FROM table
ORDER BY IDX FETCH FIRST 1 ROWS ONLY
Thanks Tim
Select a random record with Oracle:
SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1
I'd say hash it!
http://www.codinghorror.com/blog/archives/000935.html
Instead of keeping a table of all possible values, just keep a table of the values you've used. Use the random function to generate 6 random values, 1 to 26, make the string from that and save it in an array or table. If it already exists, you can (a) generate another string, or (b) move through the table to the next available (missing) 6-letter string and use that value. (b) will be more efficient as the table fills.
Following the idea of Reed Copsey's answer, i present the following code:
class IDGetter
{
private StringID ID = new StringID();
public string GetCurrentID()
{
string retStr = "";
if (ID.char1 > 51)
id.char1 = 0;
if (ID.char2 > 51)
id.char2 = 0;
if (ID.char3 > 51)
id.char3 = 0;
if (ID.char4 > 51)
id.char4 = 0;
if (ID.char5 > 51)
id.char5 = 0;
if (ID.char6 > 51)
throw new Exception("the maximum number of id's has been reached");
return ToIDChar(ID.char1) + ToIDChar(ID.char2) + ToIDChar(ID.char3) + ToIDChar(ID.char4) + ToIDChar(ID.char5) + ToIDChar(ID.char6)
id.char1++;
}
public void SetCurrentID(StringID id) //for setting the current ID from storage or resetting it or something
{
this.ID = id;
}
private const string alphabet = "abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static string ToIDChar(int number)
{
if (number > 51 || number < 0)
{
throw new InvalidArgumentException("the number passed in (" + number + ") must be between the range 0-51");
}
return alphabet[number];
}
}
public struct StringID
{
public int char1 = 0;
public int char2 = 0;
public int char3 = 0;
public int char4 = 0;
public int char5 = 0;
public int char6 = 0;
}
You might want to come up with a method of storing the current ID but that ought to work.
I used this to do something very similar. I was not to worried about the speed of it as it was going to be a rarely used event and table. But it's possible to then increase the string as needed.
/// Generates a string and checks for existance
/// <returns>Non-existant string as ID</returns>
public static string GetRandomNumbers(int numChars, string Type)
{
string result = string.Empty;
bool isUnique = false;
while (!isUnique)
{
//Build the string
result = MakeID(numChars);
//Check if unsued
isUnique = GetValueExists(result, Type);
}
return result;
}
/// Builds the string
public static string MakeID(int numChars)
{
string random = string.Empty;
string[] chars = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };
Random rnd = new Random();
for (int i = 0; i < numChars; i++)
{
random += chars[rnd.Next(0, 35)];
}
return random;
}
/// Checks database tables based on type for existance, if exists then retry
/// <returns>true or false</returns>
private static bool GetValueExists(string value, string Type)
{
bool result = false;
string sql = "";
if (Type == "URL")
{
sql = string.Format(#"IF EXISTS (SELECT COUNT(1) FROM myTable WHERE uniqueString = '{0}')
BEGIN
SELECT 1
END
ELSE
BEGIN
SELECT 0
END ", value);
}
//query the DB to see if it's in use
result = //ExecuteSQL
return result;
}

Categories