Related
In my Asp.Net MVC 5 project I use Entity Framework code first to work with MS SQL database. Suppose this is the table:
public class Ticket
{
[Key]
public int Id { get; set; }
[Required]
public string ReferenceCode { get; set; }
//Rest of the table
}
In this table, whenever I add a new code I want the ReferenceCode column to be a unique and random AlphaNumeric (containing only letters and digits) string with a specific length. This will allow users to refer to a specific ticket for instance.
These are some examples with 10 character lenght: TK254X26W1, W2S564Z111, 1STT135PA5...
Right now, I'm able to generate random strings with the given length. However, I'm not sure how to guarantee their uniqueness. I do it like this:
db.Tickets.Add(new Ticket()
{
ReferenceCode = GenerateRandomCode(10),
//...
});
To be exact, I want the GenerateRandomCode function or maybe another method to be able to make sure the generated string has not been used for another record.
I can use a for loop to check each generated code but I don't think it's a good idea. Especially after a while when the table will have thousands of records.
You can use Guid in order to generate unique (but not that random when it comes to security) keys.
Pulling from this SO question:
Guid g = Guid.NewGuid();
string GuidString = Convert.ToBase64String(g.ToByteArray());
GuidString = GuidString.Replace("=","");
GuidString = GuidString.Replace("+","");
GuidString = GuidString.ToUpper();
will generate a unique key to fit your ReferenceCode property needs but longer (22 characters). Collapsing it and using a X characters would no longer guarantee its uniqueness.
OZVV5TPP4U6XJTHACORZEQ
Mind an off-the-beaten-path solution? You've got two needs, that I can see:
Randomness. You can't have a "deterministic" function, because if someone can guess the algorithm, they could figure out everyone elses' ticket numbers.
Uniqueness. You can't have any duplicate ticket nums - which makes Random a bit difficult (you'll have to account for collisions and retry.)
But there's no reason you can't do both - you've got plenty of bit-space with 36^10. You could dedicate 4 bytes to Uniqueness, and 6 bytes to Randomness. Here's some sample code:
public partial class Form1 : Form
{
private static Random random = new Random();
private static int largeCoprimeNumber = 502277;
private static int largestPossibleValue = 1679616; // 36 ^ 4
private static char[] Base36Alphabet = new char[] { '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' };
public static string GetTicket(int id)
{
int adjustedID = id * largeCoprimeNumber % largestPossibleValue;
string ticket = IntToString(adjustedID);
while (ticket.Length < 4) ticket = "0" + ticket;
return ticket + new string(Enumerable.Repeat(Base36Alphabet, 6) .Select(s => s[random.Next(s.Length)]).ToArray());
}
private static string IntToString(int value)
{
string result = string.Empty;
int targetBase = Base36Alphabet.Length;
do
{
result = Base36Alphabet[value % targetBase] + result;
value = value / targetBase;
}
while (value > 0);
return result;
}
Quick rundown on what the code's doing. You're passing in your int id - which it then hashes in such a way that it looks random, but is guaranteed to never repeat a number for the first 1.68 million entries.
It then takes this hashed int value, and turns it into a 4-digit code; this is the "uniqueness part" - you're guaranteed a different 4 digit code at the beginning of the first 1.68 million IDs (the magic of coprime numbers.)
That leaves 6 more characters to play with. Just fill them in with random characters - that makes the whole 10-digit code awfully difficult to guess.
This solves both of your problems. It's guaranteed to be unique for the first million+ records. And it's not really "guessable" by the client, since even if they guessed the algorithm, they'd have 2 billion different possibilities for any given ID they wanted to crack.
Here's my approach that guarantees uniqueness and introduces some randomness.
Use a sequence generator that is guaranteed to give a unique number. Since you're working with SQL Server, this can be an IDENTITY column's value. You could alternatively increment an application-level value within your C# code to achieve this.
Generate a random integer to bring in some randomness to the result. This could be done with Random.Next() and any seed, even the number generated in the preceding step.
Use a method EncodeInt32AsString to convert the integers from the previous two steps into two strings (one is the unique string, one the random string). The method returns a string composed of only the allowed characters specified in the method. The logic of this method is similar to how number conversion between different bases takes place (for example, change the allowed string to only 0-9, or only 0-9A-F to get the decimal/hex representations). Therefore, the result is a "number" composed of the "digits" in allowedList.
Concatenate the strings returned. Keep the entire unique string as-is (to guarantee uniqueness) and add as many characters from the random string to pad the total length to the desired length. If required, this concatenation can be fancy, by injecting characters from the random string at random points into the unique string.
By retaining the entire unique string, this ensures uniqueness of the final result.
By using a random string, this introduces randomness. Randomness cannot be guaranteed in case the target string's length is very close to the length of the unique string.
In my testing, calling EncodeInt32AsString for Int32.MaxValue returns a unique string 6 characters long:
2147483647: ZIK0ZJ
On that basis, a target string length of 12 will be ideal, though 10 is also reasonable.
The EncodeInt32AsString Method
/// <summary>
/// Encodes the 'input' parameter into a string of characters defined by the allowed list (0-9, A-Z)
/// </summary>
/// <param name="input">Integer that is to be encoded as a string</param>
/// <param name="maxLength">If zero, the string is returned as-is. If non-zero, the string is truncated to this length</param>
/// <returns></returns>
static String EncodeInt32AsString(Int32 input, Int32 maxLength = 0)
{
// List of characters allowed in the target string
Char[] allowedList = new Char[] {
'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' };
Int32 allowedSize = allowedList.Length;
StringBuilder result = new StringBuilder(input.ToString().Length);
Int32 moduloResult;
while (input > 0)
{
moduloResult = input % allowedSize;
input /= allowedSize;
result.Insert(0, allowedList[moduloResult]);
}
if (maxLength > result.Length)
{
result.Insert(0, new String(allowedList[0], maxLength - result.Length));
}
if (maxLength > 0)
return result.ToString().Substring(0, maxLength);
else
return result.ToString();
}
The GetRandomizedString Method
Now, the preceding method just takes care of encoding a string. In order to achieve the uniqueness and randomness properties, the following logic (or similar) can be used.
In the comments, Kevin pointed out the following risk with the implementation of the EncodeInt32AsString method:
The code needs to be tweaked so that it returns a fixed-length string.
Otherwise, you can never be guaranteed of the final result is unique.
If it helps, picture one value generating ABCDE (Unique) +
F8CV1 (Random)... and then later on, another value generating
ABCDEF (Unique) + 8CV1 (Random). Both values are ABCDEF8CV1
This is a very valid point, and this has been addressed in the following GetRandomizedString method, by specifying lengths for the unique and random strings. The EncodeInt32AsString method has also been modified to pad out the return value to a specified length.
// Returns a string that is the encoded representation of the input number, and a random value
static String GetRandomizedString(Int32 input)
{
Int32 uniqueLength = 6; // Length of the unique string (based on the input)
Int32 randomLength = 4; // Length of the random string (based on the RNG)
String uniqueString;
String randomString;
StringBuilder resultString = new StringBuilder(uniqueLength + randomLength);
// This might not be the best way of seeding the RNG, so feel free to replace it with better alternatives.
// Here, the seed is based on the ratio of the current time and the input number. The ratio is flipped
// around (i.e. it is either M/N or N/M) to ensure an integer is returned.
// Casting an expression with Ticks (Long) to Int32 results in truncation, which is fine since this is
// only a seed for an RNG
Random randomizer = new Random(
(Int32)(
DateTime.Now.Ticks + (DateTime.Now.Ticks > input ? DateTime.Now.Ticks / (input + 1) : input / DateTime.Now.Ticks)
)
);
// Get a random number and encode it as a string, limit its length to 'randomLength'
randomString = EncodeInt32AsString(randomizer.Next(1, Int32.MaxValue), randomLength);
// Encode the input number and limit its length to 'uniqueLength'
uniqueString = EncodeInt32AsString(input, uniqueLength);
// For debugging/display purposes alone: show the 2 constituent parts
resultString.AppendFormat("{0}\t {1}\t ", uniqueString, randomString);
// Take successive characters from the unique and random strings and
// alternate them in the output
for (Int32 i = 0; i < Math.Min(uniqueLength, randomLength); i++)
{
resultString.AppendFormat("{0}{1}", uniqueString[i], randomString[i]);
}
resultString.Append((uniqueLength < randomLength ? randomString : uniqueString).Substring(Math.Min(uniqueLength, randomLength)));
return resultString.ToString();
}
Sample Output
Calling the above method for a variety of input values results in:
Input Int Unique String Random String Combined String
------------ ----------------- -------------- ---------------------
-10 000000 CRJM 0C0R0J0M00
0 000000 33VT 03030V0T00
1 000001 DEQK 0D0E0Q0K01
2147 0001NN 6IU8 060I0U18NN
21474 000GKI VNOA 0V0N0OGAKI
214748 004LP8 REVP 0R0E4VLPP8
2147483 01A10B RPUM 0R1PAU1M0B
21474836 0CSA38 RNL5 0RCNSLA538
214748364 3JUSWC EP3U 3EJPU3SUWC
2147483647 ZIK0ZJ BM2X ZBIMK20XZJ
1 000001 QTAF 0Q0T0A0F01
2 000002 GTDT 0G0T0D0T02
3 000003 YMEA 0Y0M0E0A03
4 000004 P2EK 0P020E0K04
5 000005 17CT 01070C0T05
6 000006 WH12 0W0H010206
7 000007 SHP0 0S0H0P0007
8 000008 DDNM 0D0D0N0M08
9 000009 192O 0109020O09
10 00000A KOLD 0K0O0L0D0A
11 00000B YUIN 0Y0U0I0N0B
12 00000C D8IO 0D080I0O0C
13 00000D KGB7 0K0G0B070D
14 00000E HROI 0H0R0O0I0E
15 00000F AGBT 0A0G0B0T0F
As can be seen above, the unique string is predictable for sequential numbers, given it is just the same number represented in a different base. However, the random string brings in some entropy to prevent users from guessing subsequent numbers. Moreover, by interleaving the "digits" of the unique string and random string it becomes slightly more difficult for users to observe any pattern.
In the above example, the length of the unique string is set to 6 (since that allows it to represent Int32.MaxValue), but the length of the random string is set to 4 because the OP wanted a total length of 10 characters.
You can achieve absolute uniqueness on the machine using UuidCreateSequential method(which deals with Uuid) in rpcrt4.dll as below. Check this link from microsoft to be sure about uniqueness. You will never get the same Id twice on your machine or the host where you upload your website.
The output format from following code is what Asp.Net MVC uses to create unique id for AspNetUsers table:
using System;
using System.Runtime.InteropServices;
public class SqlGuidUtil
{
[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);
public static Guid NewSequentialId()
{
Guid guid;
UuidCreateSequential(out guid);
var s = guid.ToByteArray();
var t = new byte[16];
t[3] = s[0];
t[2] = s[1];
t[1] = s[2];
t[0] = s[3];
t[5] = s[4];
t[4] = s[5];
t[7] = s[6];
t[6] = s[7];
t[8] = s[8];
t[9] = s[9];
t[10] = s[10];
t[11] = s[11];
t[12] = s[12];
t[13] = s[13];
t[14] = s[14];
t[15] = s[15];
return new Guid(t);
}
}
Usage:
Guid gid = SqlGuidUtil.NewSequentialId();
String sid = SqlGuidUtil.NewSequentialId().ToString();
Sample output:
637E3E78-23F5-E611-8278-506313F91120
This format is exactly the same as AspNet Identity user Id format.
You can also remove dashes(not a good idea) as below:
String sid = SqlGuidUtil.NewSequentialId().ToString().Replace("-","");
Put a unique index on the DB column, and keep generating until the DB accepts it without a unique constraint. Collisions will be very rare.
We needed to implement something like this for a different purpose in of our previous projects. We just pregenerated a number of unique identifiers into a new table (let's call it table A), and then when we wanted to insert a new record into table B, we just added the top 1 record from table A in a trigger.
user your code like
string referenceCode=Guid.NewGuid().ToString();
referenceCode=referenceCode.Replace('-', '');
db.Tickets.Add(new Ticket()
{
ReferenceCode = referenceCode;
//...
});
Try this one. It's work for me
var buffer = new byte[5];
new Random().NextBytes(buffer);
Console.WriteLine(string.Join("", buffer.Select(b => b.ToString("X2"))));
Using your ID for granting uniqueness and the System.Random class to get you a randomness you may expect something like :
private string GenerateRandomCode(int Key)
{
Random rnd = new Random(Key);
char[] values = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToArray();
string result = string.Empty;
for(int i = 0; i < 10; i++)
{
result += values[rnd.Next(0, values.Length)];
}
return result;
}
The key value will ensure the same generated code and the random class has not enough period to worry about unicity.
Try this:
Guid.NewGuid().ToString("N").Substring(0, 10)
I used this while generating random strings for ID in SQL tables from C# codebase. This relies on randomness of C# Guid and everytime you will get a new alphanumeric String.
Using Guid I have create a function to generate unique string. Of course GUIDs can collide, so I am altering the string in the middle with new Guid.
static string GenerateRandomCode(){
string guid = Guid.NewGuid().ToString("N");
List<char> lst = new List<char>();
int count = 1;
foreach(char c in guid){
if(count==11) break;
if(count % 2 ==0){
lst.Add(Guid.NewGuid().ToString().ToCharArray()[1]);
}
else{
lst.Add(c);
}
count++;
}
return string.Join("",lst.ToArray());
}
Maybe this will help you
DECLARE #userReportId BIGINT
SET #userReportId = FLOOR(RAND()*(10000000000000-1) + 1);
I've been trying to solve this interview problem which asks to shuffle a string so that no two adjacent letters are identical
For example,
ABCC -> ACBC
The approach I'm thinking of is to
1) Iterate over the input string and store the (letter, frequency)
pairs in some collection
2) Now build a result string by pulling the highest frequency (that is > 0) letter that we didn't just pull
3) Update (decrement) the frequency whenever we pull a letter
4) return the result string if all letters have zero frequency
5) return error if we're left with only one letter with frequency greater than 1
With this approach we can save the more precious (less frequent) letters for last. But for this to work, we need a collection that lets us efficiently query a key and at the same time efficiently sort it by values. Something like this would work except we need to keep the collection sorted after every letter retrieval.
I'm assuming Unicode characters.
Any ideas on what collection to use? Or an alternative approach?
You can sort the letters by frequency, split the sorted list in half, and construct the output by taking letters from the two halves in turn. This takes a single sort.
Example:
Initial string: ACABBACAB
Sort: AAAABBBCC
Split: AAAA+BBBCC
Combine: ABABABCAC
If the number of letters of highest frequency exceeds half the length of the string, the problem has no solution.
Why not use two Data Structures: One for sorting (Like a Heap) and one for key retrieval, like a Dictionary?
The accepted answer may produce a correct result, but is likely not the 'correct' answer to this interview brain teaser, nor the most efficient algorithm.
The simple answer is to take the premise of a basic sorting algorithm and alter the looping predicate to check for adjacency rather than magnitude. This ensures that the 'sorting' operation is the only step required, and (like all good sorting algorithms) does the least amount of work possible.
Below is a c# example akin to insertion sort for simplicity (though many sorting algorithm could be similarly adjusted):
string NonAdjacencySort(string stringInput)
{
var input = stringInput.ToCharArray();
for(var i = 0; i < input.Length; i++)
{
var j = i;
while(j > 0 && j < input.Length - 1 &&
(input[j+1] == input[j] || input[j-1] == input[j]))
{
var tmp = input[j];
input[j] = input[j-1];
input[j-1] = tmp;
j--;
}
if(input[1] == input[0])
{
var tmp = input[0];
input[0] = input[input.Length-1];
input[input.Length-1] = tmp;
}
}
return new string(input);
}
The major change to standard insertion sort is that the function has to both look ahead and behind, and therefore needs to wrap around to the last index.
A final point is that this type of algorithm fails gracefully, providing a result with the fewest consecutive characters (grouped at the front).
Since I somehow got convinced to expand an off-hand comment into a full algorithm, I'll write it out as an answer, which must be more readable than a series of uneditable comments.
The algorithm is pretty simple, actually. It's based on the observation that if we sort the string and then divide it into two equal-length halves, plus the middle character if the string has odd length, then corresponding positions in the two halves must differ from each other, unless there is no solution. That's easy to see: if the two characters are the same, then so are all the characters between them, which totals ⌈n/2⌉+1 characters. But a solution is only possible if there are no more than ⌈n/2⌉ instances of any single character.
So we can proceed as follows:
Sort the string.
If the string's length is odd, output the middle character.
Divide the string (minus its middle character if the length is odd) into two equal-length halves, and interleave the two halves.
At each point in the interleaving, since the pair of characters differ from each other (see above), at least one of them must differ from the last character output. So we first output that character and then the corresponding one from the other half.
The sample code below is in C++, since I don't have a C# environment handy to test with. It's also simplified in two ways, both of which would be easy enough to fix at the cost of obscuring the algorithm:
If at some point in the interleaving, the algorithm encounters a pair of identical characters, it should stop and report failure. But in the sample implementation below, which has an overly simple interface, there's no way to report failure. If there is no solution, the function below returns an incorrect solution.
The OP suggests that the algorithm should work with Unicode characters, but the complexity of correctly handling multibyte encodings didn't seem to add anything useful to explain the algorithm. So I just used single-byte characters. (In C# and certain implementations of C++, there is no character type wide enough to hold a Unicode code point, so astral plane characters must be represented with a surrogate pair.)
#include <algorithm>
#include <iostream>
#include <string>
// If possible, rearranges 'in' so that there are no two consecutive
// instances of the same character.
std::string rearrange(std::string in) {
// Sort the input. The function is call-by-value,
// so the argument itself isn't changed.
std::string out;
size_t len = in.size();
if (in.size()) {
out.reserve(len);
std::sort(in.begin(), in.end());
size_t mid = len / 2;
size_t tail = len - mid;
char prev = in[mid];
// For odd-length strings, start with the middle character.
if (len & 1) out.push_back(prev);
for (size_t head = 0; head < mid; ++head, ++tail)
// See explanatory text
if (in[tail] != prev) {
out.push_back(in[tail]);
out.push_back(prev = in[head]);
}
else {
out.push_back(in[head]);
out.push_back(prev = in[tail]);
}
}
}
return out;
}
you can do that by using a priority queue.
Please find the below explanation.
https://iq.opengenus.org/rearrange-string-no-same-adjacent-characters/
Here is a probabilistic approach. The algorithm is:
10) Select a random char from the input string.
20) Try to insert the selected char in a random position in the output string.
30) If it can't be inserted because of proximity with the same char, go to 10.
40) Remove the selected char from the input string and go to 10.
50) Continue until there are no more chars in the input string, or the failed attempts are too many.
public static string ShuffleNoSameAdjacent(string input, Random random = null)
{
if (input == null) return null;
if (random == null) random = new Random();
string output = "";
int maxAttempts = input.Length * input.Length * 2;
int attempts = 0;
while (input.Length > 0)
{
while (attempts < maxAttempts)
{
int inputPos = random.Next(0, input.Length);
var outputPos = random.Next(0, output.Length + 1);
var c = input[inputPos];
if (outputPos > 0 && output[outputPos - 1] == c)
{
attempts++; continue;
}
if (outputPos < output.Length && output[outputPos] == c)
{
attempts++; continue;
}
input = input.Remove(inputPos, 1);
output = output.Insert(outputPos, c.ToString());
break;
}
if (attempts >= maxAttempts) throw new InvalidOperationException(
$"Shuffle failed to complete after {attempts} attempts.");
}
return output;
}
Not suitable for strings longer than 1,000 chars!
Update: And here is a more complicated deterministic approach. The algorithm is:
Group the elements and sort the groups by length.
Create three empty piles of elements.
Insert each group to a separate pile, inserting always the largest group to the smallest pile, so that the piles differ in length as little as possible.
Check that there is no pile with more than half the total elements, in which case satisfying the condition of not having same adjacent elements is impossible.
Shuffle the piles.
Start yielding elements from the piles, selecting a different pile each time.
When the piles that are eligible for selection are more than one, select randomly, weighting by the size of each pile. Piles containing near half of the remaining elements should be much preferred. For example if the remaining elements are 100 and the two eligible piles have 49 and 40 elements respectively, then the first pile should be 10 times more preferable than the second (because 50 - 49 = 1 and 50 - 40 = 10).
public static IEnumerable<T> ShuffleNoSameAdjacent<T>(IEnumerable<T> source,
Random random = null, IEqualityComparer<T> comparer = null)
{
if (source == null) yield break;
if (random == null) random = new Random();
if (comparer == null) comparer = EqualityComparer<T>.Default;
var grouped = source
.GroupBy(i => i, comparer)
.OrderByDescending(g => g.Count());
var piles = Enumerable.Range(0, 3).Select(i => new Pile<T>()).ToArray();
foreach (var group in grouped)
{
GetSmallestPile().AddRange(group);
}
int totalCount = piles.Select(e => e.Count).Sum();
if (piles.Any(pile => pile.Count > (totalCount + 1) / 2))
{
throw new InvalidOperationException("Shuffle is impossible.");
}
piles.ForEach(pile => Shuffle(pile));
Pile<T> previouslySelectedPile = null;
while (totalCount > 0)
{
var selectedPile = GetRandomPile_WeightedByLength();
yield return selectedPile[selectedPile.Count - 1];
selectedPile.RemoveAt(selectedPile.Count - 1);
totalCount--;
previouslySelectedPile = selectedPile;
}
List<T> GetSmallestPile()
{
List<T> smallestPile = null;
int smallestCount = Int32.MaxValue;
foreach (var pile in piles)
{
if (pile.Count < smallestCount)
{
smallestPile = pile;
smallestCount = pile.Count;
}
}
return smallestPile;
}
void Shuffle(List<T> pile)
{
for (int i = 0; i < pile.Count; i++)
{
int j = random.Next(i, pile.Count);
if (i == j) continue;
var temp = pile[i];
pile[i] = pile[j];
pile[j] = temp;
}
}
Pile<T> GetRandomPile_WeightedByLength()
{
var eligiblePiles = piles
.Where(pile => pile.Count > 0 && pile != previouslySelectedPile)
.ToArray();
Debug.Assert(eligiblePiles.Length > 0, "No eligible pile.");
eligiblePiles.ForEach(pile =>
{
pile.Proximity = ((totalCount + 1) / 2) - pile.Count;
pile.Score = 1;
});
Debug.Assert(eligiblePiles.All(pile => pile.Proximity >= 0),
"A pile has negative proximity.");
foreach (var pile in eligiblePiles)
{
foreach (var otherPile in eligiblePiles)
{
if (otherPile == pile) continue;
pile.Score *= otherPile.Proximity;
}
}
var sumScore = eligiblePiles.Select(p => p.Score).Sum();
while (sumScore > Int32.MaxValue)
{
eligiblePiles.ForEach(pile => pile.Score /= 100);
sumScore = eligiblePiles.Select(p => p.Score).Sum();
}
if (sumScore == 0)
{
return eligiblePiles[random.Next(0, eligiblePiles.Length)];
}
var randomScore = random.Next(0, (int)sumScore);
int accumulatedScore = 0;
foreach (var pile in eligiblePiles)
{
accumulatedScore += (int)pile.Score;
if (randomScore < accumulatedScore) return pile;
}
Debug.Fail("Could not select a pile randomly by weight.");
return null;
}
}
private class Pile<T> : List<T>
{
public int Proximity { get; set; }
public long Score { get; set; }
}
This implementation can suffle millions of elements. I am not completely convinced that the quality of the suffling is as perfect as the previous probabilistic implementation, but should be close.
func shuffle(str:String)-> String{
var shuffleArray = [Character](str)
//Sorting
shuffleArray.sort()
var shuffle1 = [Character]()
var shuffle2 = [Character]()
var adjacentStr = ""
//Split
for i in 0..<shuffleArray.count{
if i > shuffleArray.count/2 {
shuffle2.append(shuffleArray[i])
}else{
shuffle1.append(shuffleArray[i])
}
}
let count = shuffle1.count > shuffle2.count ? shuffle1.count:shuffle2.count
//Merge with adjacent element
for i in 0..<count {
if i < shuffle1.count{
adjacentStr.append(shuffle1[i])
}
if i < shuffle2.count{
adjacentStr.append(shuffle2[i])
}
}
return adjacentStr
}
let s = shuffle(str: "AABC")
print(s)
I want to easily pre-populate a single dimensional string array which I am calling "letters" with the values:
AAAAAA
AAAAAB
AAAAAC
AAAAAD
..
..
ZZZZZX
ZZZZZY
ZZZZZZ
Thats 165 million combinations in order.
The idea being I need to then be able to ask for any particular combination of 6 characters such as BBCHHJ and use Array.Index to return the element of the array it is in.
I have the second bit fine:
String searchFor;
Console.Write("Enter a string value to search for: ");
searchFor = Console.ReadLine();
int indexValue = Array.IndexOf(letters, searchFor);
Console.WriteLine("The value you are after is in element index: " + indexValue);
Console.ReadLine();
But I have no idea how to easily initialise the letters array with all those combinations, in order!
A variation on Jakub's answer which should be a bit more efficient:
int result = s
.Select(c => c - 'A') // map 'A'-'Z' to 0-25
.Aggregate(0, (total, next) => total * 26 + next); // calculate the base 26 value
This has the advantage of avoiding the Reverse and the separate Sum, and the powers of 26 don't have to be calculated from scratch in each iteration.
Storing 308 million elements in array and searching them is not the best solution, rather calculate the index at runtime. I have created a code sample:
string input = "ZZZZZZ";
//default values
string alphabets_s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char[] alphabets = alphabets_s.ToCharArray();
int result = 1; //starting with "one" because zero will make everything zero
//calculating index
for (int i = 0; i < input.Length; i++)
{
//get character index and add "1" to avoid multiplication with "0"
int index = Array.IndexOf(alphabets, input[i]) + 1;
//multiply it with the current result
result *= index;
}
//subtract 1 from final result, because we started it with 1
result--;
PS: I did just basic testing, please inform me if you find anything wrong in it.
As I wrote in a comment, what you're trying to achieve is basically conversion from base 26 number.
The first step is to convert the string to a list of digits. Then just multiply by powers of 26 and add together:
var s = "AAAABB";
var result = s
.Select(c => c - 'A') //map characters to numbers: A -> 0, B -> 1 etc
.Reverse() //reverse the sequence to have the least significant digit first
.Select((d, i) => d * Math.Pow(26, i))
.Sum();
I tried to create a function for print all the articles of a billing with some max length vars, but when this max length is exceeded Index out of range errors appears or in another cases total and quantity Doesn't appear in line:
Max Length variables:
private Int32 MaxCharPage = 36;
private Int32 MaxCharArticleName = 15;
private Int32 MaxCharArticleQuantity = 4;
private Int32 MaxCharArticleSellPrice = 6;
private Int32 MaxCharArticleTotal = 8;
private Int32 MaxCharArticleLineSpace = 1;
This is my current function:
private IEnumerable<String> ArticleLine(Double Quantity, String Name, Double SellPrice, Double Total)
{
String QuantityChunk = (String)(Quantity).ToString("#,##0.00");
String PriceChunk = (String)(SellPrice).ToString("#,##0.00");
String TotalChunk = (String)(Total).ToString("#,##0.00");
// full chunks with "size" length
for (int i = 0; i < Name.Length / this.MaxCharArticleName; ++i)
{
String Chunk = String.Empty;
if (i == 0)
{
Chunk = QuantityChunk + new String((Char)32, (MaxCharArticleQuantity + MaxCharArticleLineSpace - QuantityChunk.Length)) +
Name.Substring(i * this.MaxCharArticleName, this.MaxCharArticleName) +
new String((Char)32, (MaxCharArticleLineSpace)) +
new String((Char)32, (MaxCharArticleSellPrice - PriceChunk.Length)) +
PriceChunk +
new String((Char)32, (MaxCharArticleLineSpace)) +
new String((Char)32, (MaxCharArticleTotal - TotalChunk.Length)) +
TotalChunk;
}
else
{
Chunk = new String((Char)32, (MaxCharArticleQuantity + MaxCharArticleLineSpace)) +
Name.Substring(i * this.MaxCharArticleName, this.MaxCharArticleName);
}
yield return Chunk;
}
if (Name.Length % this.MaxCharArticleName > 0)
{
String chunk = Name.Substring(Name.Length / this.MaxCharArticleName * this.MaxCharArticleName);
yield return new String((Char)32, (MaxCharArticleQuantity + MaxCharArticleLineSpace)) + chunk;
}
}
private void AddArticle(Double Quantity, String Name, Double SellPrice, Double Total)
{
Lines.AddRange(ArticleLine(Quantity, Name, SellPrice, Total).ToList());
}
For example:
private List<String> Lines = new List<String>();
private void Form1_Load(object sender, EventArgs e)
{
AddArticle(2.50, "EGGS", 0.50, 1.25); // Problem: Dont show the numbers like (Quantity, Sellprice, Total)
//AddArticle(100.52, "HAND SOAP /w EP", 5.00, 502.60); //OutOfRangeException
AddArticle(105.6, "LONG NAME ARTICLE DESCRIPTION", 500.03, 100.00);
//AddArticle(100, "LONG NAME ARTICLE DESCRIPTION2", 1500.03, 150003.00); // OutOfRangeException
foreach (String line in Lines)
{
Console.WriteLine(line);
}
}
Console Output:
LINE1: EGGS
LINE2:105.6LONG NAME ARTIC 500.03 100.00
LINE3: LE DESCRIPTION
Desired output:
LINE:2.50 EGGS 0.50 1.25
LINE:100. HAND SOAP /w EP 5.00 502.60
LINE: 52
LINE:105. LONG NAME ARTIC 500.03 100.00
LINE: 60 LE DESCRIPTION
LINE:100. LONG NAME ARTIC 1,500. 150,003.
LINE: 00 LE DESCRIPTION2 03 00
You are attempting to take values (string values, ultimately) and extract specified lengths from them (chunks), where the each individual chunk of a given group goes on one line, and the next chunk(s) go on the next line(s). There are several challenges in your posted code. One of the biggest is that you may not understand what the / operator does. It is, of course, used for division, but it is integer division, meaning you get a whole number as the result. E.g. 3 / 15 gives you 0, whereas 17 / 15 would give you 1. This means your loop never runs unless the length of the value is greater than the specified chunk limit.
Another possible issue is that you only perform this check against Name, not the other items (though you may have omitted the code for them for brevity reasons).
Your current code is also creating a lot of unnecessary strings, which will lead to performance degradation. Remember that in C# strings are immutable - meaning they cannot be changed. When you "change" the value of a string, you are actually creating another copy of the string.
The crux of your requirement is how to "chunk" the values in such a way that the correct output is achieved. One way to do this is to create an extension method that will take a string value and "chunkify" it for you. One such example is found here: Split String Into Array of Chunks. I've modified it slightly to use a List<T>, but an array would work just as well.
public static List<string> SplitIntoChunks(this string toSplit, int chunkSize)
{
int stringLength = toSplit.Length;
int chunksRequired = (int)Math.Ceiling(decimal)stringLength / (decimal)chunkSize);
List<string> chunks = new List<string>();
int lengthRemaining = stringLength;
for (int i = 0; i < chunksRequired; i++)
{
int lengthToUse = Math.Min(lengthRemaining, chunkSize);
int startIndex = chunkSize * i;
chunks.Add(toSplit.Substring(startIndex, lengthToUse));
lengthRemaining = lenghtRemaining - lengthToUse;
}
return chunks;
}
Say you have a string named myString. You would use the above method like this: string[] chunks = myString.SplitIntoChunks(15);, and you would receive an array of 15 character strings (depending on the size of the string).
A quick walk-through of the code (as there is not much explanation on the page). The size of the chunk is passed into the extension method. The length of the string is recorded, and the number of chunks for that string is determined using the Math.Ceiling function.
Then a for loop is constructed with the number of chunks required as the limit. Inside the loop, the length of the chunk is determined (using the lower of either the chunk size or the remaining length of the string), the starting index is calculated based on the chunk size and the loop index, and then the chunk is extracted via Substring. Finally remaining length is calculated, and once the loop ends the chunks are returned.
One way to use this extension method in your code would look like this. The extension method will need to be in a separate, static class (I suggest building a library that has a class dedicated solely to extension methods, as they come in quite handy). Note that I haven't had time to test this, and it's a bit kludgy for my tastes, but it should at least get you going in the right direction.
private IEnumerable<string> ArticleLine(double quantity, string name, double sellPrice, double total)
{
List<string> quantityChunks = quantity.ToString("#,##0.00").SplitIntoChunks(maxCharArticleQuantity);
List<string> nameChunks = name.SplitIntoChunks(maxCharArticleName);
List<string> sellPriceChunks = sellPrice.ToString("#,##0.00").SplitIntoChunks(maxCharArticleSellPrice);
List<string> totalChunks = total.ToString("#,##0.00").SplitIntoChunks(maxCharArticleTotal);
int maxLines = (new List<int>() { quantityChunks.Count,
nameChunks.Count,
sellPriceChunks.Count,
totalChunks.Count }).Max();
for (int i = 0; i < maxLines; i++)
{
lines.Add(String.Format("{0}{1}{2}{3}",
quantityChunks.Count > i ?
quantityChunks[i].PadLeft(maxCharArticleQuantity) :
String.Empty.PadLeft(maxCharArticleQuantity),
nameChunks.Count > i ?
nameChunks[i].PadLeft(maxCharArticleName) :
String.Empty.PadLeft(maxCharArticleName, ' '),
sellPriceChunks.Count > i ?
sellPriceChunks[i].PadLeft(maxCharArticleSellPrice) :
String.Empty.PadeLeft(maxCharArticleSellPrice),
totalChunks.Count > i ?
totalChunks[i].PadLeft(maxCharArticleTotal) :
String.Empty.PadLeft(maxCharArticleTotal));
}
return lines;
}
The above code does a couple of things. First, it calls SplitIntoChunks on each of the four variables.
Next, it uses the Max() extension method (you'll need to add a reference to System.Linq if you don't already have one, plus a using directive). to determine the maximum number of lines needed (based on the highest count of the four lists).
Finally, it uses a 4for loop to build the lines. Note that I use the ternary operator (?:) to build each line. The reason I went this route is so that the lines would be properly formatted, even if one or more of the 4 values didn't have something for that line. In other words:
quantityChunks.Count > i
If the count of items in quantityChunks is greater than i, then there is an entry for that item for that line.
quanityChunks[i].PadLeft(maxCharArticleQuantity, ' ')
If the condition evaluates to true, we use the corresponding entry (and pad it with spaces to keep alignment).
String.Empty.PadLeft(maxCharArticleQuantity, ' ')
If the condition is false, we simply put in spaces for the maximum number of characters for that position in the line.
Then you can print the output like this:
foreach(string line in lines)
{
Console.WriteLine(line);
}
The portions of the code where I check for the maximum number of lines and the use of a ternary operator in the String.Format feel very kludgy to me, but I don't have time to finesse it into something more respectable. I hope this at least points you in the right direction.
EDIT
Fixed the PadLeft syntax and removed the character specified, as space is used by default.
I'm trying to write a program in C# that takes in an int x and decides if it has exactly 7 digits. Right now I'm using x.toString().Length == 7 to check, but I noticed that if the number starts with 0, it automatically gets omitted and I get an incorrect answer (ie the program thinks the input length is less than 7)
Is there a way to fix this? Thanks in advance.
Edit: Sorry I should have mentioned, this was a program to collect and validate the format of ID numbers (so I didn't want something like 0000001 to default to 1) Thanks for the string input suggestion, I think I'm going to try that.
If you want to preserve the input formatting, you must not convert the input to an int. You must store it in a String.
You say your program takes an int. At that point you have already lost. You need to change that interface to accept String inputs.
If you don't care about leading zeros, you're really looking for 7 digits or less. You can check for:
x.toString().Length <= 7
or better:
x < 10000000
Maybe I'm wrong, but to me, 0000001 == 1, and 1 has one digit, not seven. So that's mathematically correct behaviour.
I think you could format it as a string:
int myInt=1;
myInt.ToString("0000000");
prints:
0000001.
so you could do:
if (myInt.ToString("0000000").Length==7)
You can simply write:
int input = 5;
if(input.ToString("0000000").Length == 7)
{
//do your stuff
}
No. It is perfectly valid for a numeric literal to have leading 0s, but a) many languages consider this to be an octal literal, and b) the leading 0s don't actually exist as part of the number. If you need a string then start with a string literal.
You should use string to check length count including 0.
Then I would like to ask "Why do you want to show 0000007? For What?"
You said you're asking for a int, but I suppose you're receiving it as string:
int i = 0;
string number = Console.ReadLine();
if (Int32.TryParse(number, out i))
{
//if (i.ToString().Length == 7) // you can try this too
if (i > 999999 && i < 10000000)
{
Console.WriteLine("Have exactly 7 digits");
}
else
{
Console.WriteLine("Doesn't have exactly 7 digits");
}
}
else
{
Console.WriteLine("Not an Int32 number");
}
This way you try to cast that received number as Int32 and, so, compare its length.
You can let the number be saved as an int with the omitted zeros. but then if you want the number displayed with the zeros then you can use an if statement and a while loop. for example,
Let's assume the values are stored in a numbers array and you need them to be stored as int so you can sort them but displayed as string so you can display with the leading zeros.
int[] numbers = new int[3];
numbers[0] = 001;
numbers[1] = 002;
numbers[2] = 123;
String displayed_Number;
for (int i = 0; i < numbers.Length; i++)
{
displayed_Number = numbers[i].ToString();
if (displayed_Number.Length == 3)
{
listBox.Items.Add(displayed_Number);
}
else if (displayed_Number.Length < 3)
{
while (displayed_Number.Length < 3)
{
displayed_Number = "0" + displayed_Number;
}
listBox.Items.Add(displayed_Number);
}
}
The output is 001 002 123
That way you can maintain the zeros in the numbers when displayed. and they can be stored as int in case you have to store them as int.