Auto Generate alphanumeric Unique Id with C# - c#

Total string length is 5 chars
I have a scenario, ID starts with
A0001 and ends with A9999 then
B0001 to B9999 until F0001 to f9999
after that
FA001 to FA999 then
FB001 to FB999 until ....FFFF9
Please suggest any idea on how to create this format.

public static IEnumerable<string> Numbers()
{
return Enumerable.Range(0xA0000, 0xFFFF9 - 0xA0000 + 1)
.Select(x => x.ToString("X"));
}
You could also have an id generator class:
public class IdGenerator
{
private const int Min = 0xA0000;
private const int Max = 0xFFFF9;
private int _value = Min - 1;
public string NextId()
{
if (_value < Max)
{
_value++;
}
else
{
_value = Min;
}
return _value.ToString("X");
}
}

I am a few years late. But I hope my answer will help everyone looking for a good ID Generator. None of the previous answers work as expected and do not answer this question.
My answer fits the requirements perfectly. And more!!!
Notice that setting the _fixedLength to ZERO will create dynamically sized ID's.
Setting it to anything else will create FIXED LENGTH ID's;
Notice also that calling the overload that takes a current ID will "seed" the class and consecutive calls DO NOT need to be called with another ID. Unless you had random ID's and need the next one on each.
Enjoy!
public static class IDGenerator
{
private static readonly char _minChar = 'A';
private static readonly char _maxChar = 'C';
private static readonly int _minDigit = 1;
private static readonly int _maxDigit = 5;
private static int _fixedLength = 5;//zero means variable length
private static int _currentDigit = 1;
private static string _currentBase = "A";
public static string NextID()
{
if(_currentBase[_currentBase.Length - 1] <= _maxChar)
{
if(_currentDigit <= _maxDigit)
{
var result = string.Empty;
if(_fixedLength > 0)
{
var prefixZeroCount = _fixedLength - _currentBase.Length;
if(prefixZeroCount < _currentDigit.ToString().Length)
throw new InvalidOperationException("The maximum length possible has been exeeded.");
result = result = _currentBase + _currentDigit.ToString("D" + prefixZeroCount.ToString());
}
else
{
result = _currentBase + _currentDigit.ToString();
}
_currentDigit++;
return result;
}
else
{
_currentDigit = _minDigit;
if(_currentBase[_currentBase.Length - 1] == _maxChar)
{
_currentBase = _currentBase.Remove(_currentBase.Length - 1) + _minChar;
_currentBase += _minChar.ToString();
}
else
{
var newChar = _currentBase[_currentBase.Length - 1];
newChar++;
_currentBase = _currentBase.Remove(_currentBase.Length - 1) + newChar.ToString();
}
return NextID();
}
}
else
{
_currentDigit = _minDigit;
_currentBase += _minChar.ToString();
return NextID();
}
}
public static string NextID(string currentId)
{
if(string.IsNullOrWhiteSpace(currentId))
return NextID();
var charCount = currentId.Length;
var indexFound = -1;
for(int i = 0; i < charCount; i++)
{
if(!char.IsNumber(currentId[i]))
continue;
indexFound = i;
break;
}
if(indexFound > -1)
{
_currentBase = currentId.Substring(0, indexFound);
_currentDigit = int.Parse(currentId.Substring(indexFound)) + 1;
}
return NextID();
}
}
This is a sample of the ouput using _fixedLength = 4 and _maxDigit = 5
A001
A002
A003
A004
A005
B001
B002
B003
B004
B005
C001
C002
C003
C004
C005
AA01
AA02
AA03
AA04
AA05
AB01
AB02
AB03
AB04
AB05
AC01
AC02
AC03
AC04
AC05

see this code
private void button1_Click(object sender, EventArgs e)
{
string get = label1.Text.Substring(7); //label1.text=ATHCUS-100
MessageBox.Show(get);
string ou="ATHCUS-"+(Convert.ToInt32(get)+1).ToString();
label1.Text = ou.ToString();
}

Run this query in order to get the last ID in the database
SELECT TOP 1 [ID_COLUMN] FROM [NAME_OF_TABLE] ORDER BY [ID_COLUMN] DESC
Read the result to a variable and then run the following function on the result in order to get the next ID.
public string NextID(string lastID)
{
var allLetters = new string[] {"A", "B", "C", "D", "E", "F"};
var lastLetter = lastID.Substring(0, 1);
var lastNumber = int.Parse(lastID.Substring(1));
if (Array.IndexOf(allLetters, lastLetter) < allLetters.Length - 1 &&
lastNumber == 9999)
{
//increase the letter
lastLetter = allLetters(Array.IndexOf(allLetters, lastLetter) + 1);
lastNumber = 0;
} else {
lastLetter = "!";
}
var result = lastLetter + (lastNumber + 1).ToString("0000");
//ensure we haven't exceeded the upper limit
if (result.SubString(0, 1) == "!") {
result = "Upper Bounds Exceeded!";
}
return result;
}
DISCLAIMER
This code will only generate the first set of IDs. I do not understand the process of generating the second set.

If you need to take it from the database and do this you can use something like the following.
int dbid = /* get id from db */
string id = dbid.ToString("X5");
This should give you the format you are looking for as a direct convert from the DB ID.

Related

How to auto-increment number and letter to generate a string sequence wise in c#

I have to make a string which consists a string like - AAA0009, and once it reaches AAA0009, it will generate AA0010 to AAA0019 and so on.... till AAA9999 and when it will reach to AAA9999, it will give AAB0000 to AAB9999 and so on till ZZZ9999.
I want to use static class and static variables so that it can auto increment by itself on every hit.
I have tried some but not even close, so help me out thanks.
Thanks for being instructive I was trying as I Said already but anyways you already want to put negatives over there without even knowing the thing:
Code:
public class GenerateTicketNumber
{
private static int num1 = 0;
public static string ToBase36()
{
const string base36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var sb = new StringBuilder(9);
do
{
sb.Insert(0, base36[(byte)(num1 % 36)]);
num1 /= 36;
} while (num1 != 0);
var paddedString = "#T" + sb.ToString().PadLeft(8, '0');
num1 = num1 + 1;
return paddedString;
}
}
above is the code. this will generate a sequence but not the way I want anyways will use it and thanks for help.
Though there's already an accepted answer, I would like to share this one.
P.S. I do not claim that this is the best approach, but in my previous work we made something similar using Azure Table Storage which is a no sql database (FYI) and it works.
1.) Create a table to store your running ticket number.
public class TicketNumber
{
public string Type { get; set; } // Maybe you want to have different types of ticket?
public string AlphaPrefix { get; set; }
public string NumericPrefix { get; set; }
public TicketNumber()
{
this.AlphaPrefix = "AAA";
this.NumericPrefix = "0001";
}
public void Increment()
{
int num = int.Parse(this.NumericPrefix);
if (num + 1 >= 9999)
{
num = 1;
int i = 2; // We are assuming that there are only 3 characters
bool isMax = this.AlphaPrefix == "ZZZ";
if (isMax)
{
this.AlphaPrefix = "AAA"; // reset
}
else
{
while (this.AlphaPrefix[i] == 'Z')
{
i--;
}
char iChar = this.AlphaPrefix[i];
StringBuilder sb = new StringBuilder(this.AlphaPrefix);
sb[i] = (char)(iChar + 1);
this.AlphaPrefix = sb.ToString();
}
}
else
{
num++;
}
this.NumericPrefix = num.ToString().PadLeft(4, '0');
}
public override string ToString()
{
return this.AlphaPrefix + this.NumericPrefix;
}
}
2.) Make sure you perform row-level locking and issue an error when it fails.
Here's an oracle syntax:
SELECT * FROM TICKETNUMBER WHERE TYPE = 'TYPE' FOR UPDATE NOWAIT;
This query locks the row and returns an error if the row is currently locked by another session.
We need this to make sure that even if you have millions of users generating a ticket number, it will not mess up the sequence.
Just make sure to save the new ticket number before you perform a COMMIT.
I forgot the MSSQL version of this but I recall using WITH (ROWLOCK) or something. Just google it.
3.) Working example:
static void Main()
{
TicketNumber ticketNumber = new TicketNumber();
ticketNumber.AlphaPrefix = "ZZZ";
ticketNumber.NumericPrefix = "9999";
for (int i = 0; i < 10; i++)
{
Console.WriteLine(ticketNumber);
ticketNumber.Increment();
}
Console.Read();
}
Output:
Looking at your code that you've provided, it seems that you're backing this with a number and just want to convert that to a more user-friendly text representation.
You could try something like this:
private static string ValueToId(int value)
{
var parts = new List<string>();
int numberPart = value % 10000;
parts.Add(numberPart.ToString("0000"));
value /= 10000;
for (int i = 0; i < 3 || value > 0; ++i)
{
parts.Add(((char)(65 + (value % 26))).ToString());
value /= 26;
}
return string.Join(string.Empty, parts.AsEnumerable().Reverse().ToArray());
}
It will take the first 4 characters and use them as is, and then for the remainder of the value if will convert it into characters A-Z.
So 9999 becomes AAA9999, 10000 becomes AAB0000, and 270000 becomes ABB0000.
If the number is big enough that it exceeds 3 characters, it will add more letters at the start.
Here's an example of how you could go about implementing it
void Main()
{
string template = #"AAAA00";
var templateChars = template.ToCharArray();
for (int i = 0; i < 100000; i++)
{
templateChars = IncrementCharArray(templateChars);
Console.WriteLine(string.Join("",templateChars ));
}
}
public static char Increment(char val)
{
if(val == '9') return 'A';
if(val == 'Z') return '0';
return ++val;
}
public static char[] IncrementCharArray(char[] val)
{
if (val.All(chr => chr == 'Z'))
{
var newArray = new char[val.Length + 1];
for (int i = 0; i < newArray.Length; i++)
{
newArray[i] = '0';
}
return newArray;
}
int length = val.Length;
while (length > -1)
{
char lastVal = val[--length];
val[length] = Increment(lastVal);
if ( val[length] != '0') break;
}
return val;
}

Customer Dictionary Index [duplicate]

This question already has answers here:
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 6 years ago.
I have a class named ClusterMember as:
public class ClusterMember
{
public string _name;
public ClusterMember(string _name)
{
this._name = _name;
}
public string _Name
{
get { return _name; }
set { _name = value; }
}
}
public class Cluster
{
private Dictionary<int, List<ClusterMember>> _dic;
public Cluster(Dictionary<int, List<ClusterMember>> _dic)
{
this._dic = _dic;
}
public Dictionary<int, List<ClusterMember>> _Dictionary
{
get { return _dic; }
set { _dic = value; }
}
// adding members to clusters
public void AddToCluster(int _id, ClusterMember _clusMem)
{
// checks if cluster with specific id is already in Dictionary
if (!_dic.ContainsKey(_id))
{
_dic.Add(_id, new List<ClusterMember>());
}
else
{
_dic[_id].Add(_clusMem);
}
}
// get members count for specific cluster id
public int GetCount(int id)
{
return _dic[id].Count;
}
// get members count for all clusters
public Dictionary<int, int> GetCounts()
{
return _dic.ToDictionary(k => k.Key, v => v.Value.Count);
}
public void Print_Clusters(List<Cluster> _clusterToPrint, double _alpha, int _length)
{
Console.WriteLine("\n" + "Number of Customers = " + _length + "\n");
Console.WriteLine("\n" + "Alpha = " + _alpha + "\n");
Console.WriteLine("\n" + "Number of Clusters = " + _clusterToPrint.Max() + "\n");
foreach ( var _clusters in _clusterToPrint )
{
Console.WriteLine("Cluster ID {0} - ClusterMember {1}", _clusters._Dictionary.Keys, _clusters._Dictionary.Values);
Console.Write("\n");
}
Console.ReadLine();
}
}
When I use these classes in this method as:
public static void DP_Cluster(List<string> _cust, double _alpha)
{
var _customer = new List<string>();
var _currentTables = 0; // current number of tables i.e. "k"
var _dicInitial = new Dictionary<int, List<ClusterMember>>();
var _customerNumber = 0; // running customer number i.e. "n"
var _probOld = new Dictionary<int, double>();
//var _probOld = new List<Double>(); // porbability to be in Old cluster
var _probNew = 0.0; // porbability to be in New cluster
List<Cluster> _myClusters = new List<Cluster>();
Cluster _cluster = new Cluster(_dicInitial);
// add first customer from "_cust" list directly to a new cluster
_cluster.AddToCluster(++_currentTables, new ClusterMember(_cust.ElementAt(_customerNumber)));
_myClusters.Add(_cluster);
_probOld.Add(_currentTables, Convert.ToDouble(1) / Convert.ToDouble(1 + _alpha));
for (int _i = 1; _i < _cust.Count - 1; _i++)
{
if (_i <= _currentTables)
{
// get customer Count in Cluster i / customerNumber + alpha
// Got ERROR here at "_myClusters[_i]"
double _probOldValue = _myClusters[_i].GetCount(_i) / ((_i + 1) - 1 + _alpha);
_probOld.Add(_currentTables, _probOldValue);
}
else if ( _i == _currentTables + 1)
{
_probNew =_alpha / ((_i + 1) - 1 + _alpha);
_currentTables++;
_probOld.Add(_currentTables, _probNew);
}
List<int> _keyList = new List<int>(_probOld.Keys);
Random _random = new Random();
int _randomKey = _keyList[_random.Next(_keyList.Count)];
_cluster.AddToCluster(_randomKey, new ClusterMember(_cust.ElementAt(_i)));
}
_myClusters.Add(_cluster);
_cluster.Print_Clusters(_myClusters, _alpha, _cust.Count);
}
public static List<string> GetRandomString(int _numOfStrings, int _stringLength)
{
string[] _arrStr = new string[_numOfStrings];
List<string> _listSt;
const string _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var _random = new Random();
for (int _i = 0; _i < _arrStr.Length; _i++)
{
_arrStr[_i] = new string(Enumerable.Repeat(_chars, _stringLength).Select(s => s[_random.Next(s.Length)]).ToArray());
}
_listSt = new List<string>(_arrStr);
return _listSt;
}
static int Main()
{
double _alfa = 5;
int _n = 30;
List<string> _data = GetRandomString(_n, 8);
DP_Cluster(_data, _alfa);
return 0;
}
The ERROR is:
Index was out of range. Must be non-negative and less than the size of the collection.
whereas I have added ++_currentTables in _cluster i.e. object of class Cluster here it should be 1 which is not out of range.
I'm wanting to take the Count at specific cluster id which has been added already in List whereas the error is demanding less than size of index value. I think the value which I have inserted at this line:
_cluster.AddToCluster(++_currentTables, new ClusterMember(_cust.ElementAt(_customerNumber)));
I have inserted 1 i.e. (_++currentTables) as first index of _dic member of class i.e. Cluster whereas by default it should start with 0.
Is this an error of index insertion? But I want to start the cluster number with 1 not with 0. How to sort out this error?
Blindly running code throws exception at this line:
_myCluster.Count is 1, means you can only access _myCluster[0]
Value of _i is 1, so you are trying to access _myCluster[1], therefore exception.
Error is probably that what you start _i from 1 (indexes are zero-based), perhaps you want to access _myCluster[i - 1], only you know.

Split a range sequence into multiple string c#,linq [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
Not sure why question is being marked as offtopic, where as so called desired behaviour is included within the question post!
I am trying to write this program that takes two inputs:
• a set of include intervals
• and a set of exclude intervals
The sets of intervals can be given in any order, and they may be empty or overlapping. The program should output the result of taking all the includes and “remove” the excludes. The output should be given as non-overlapping intervals in a sorted order.
Intervals will contain Integers only
Example :
Includes: 50-600, 10-100
Excludes: (empty)
Output: 10-600
Includes: 10-100, 200-300, 400-600
Excludes: 95-205, 410-420
Output: 10-94, 206-300, 400-409, 421-600
I tried to populate two Enumerable Range from include and excludes (after splitting,parsing ), but didn't find any efficient way of implementing this afterwards.
string[] _break = _string.Split(',');
string[] _breakB = _stringB.Split(',');
string[] res = new string[_break.Length + 1];
string[] _items, _itemsB;
List < int > _back = new List < int > ();
int count = 0;
foreach(var _item in _break) {
_items = _item.Split('-');
var a = Enumerable.Range(int.Parse(_items[0]), (int.Parse(_items[1]) - int.Parse(_items[0]) + 1)).ToList();
foreach(var _itemB in _breakB) {
_itemsB = _itemB.Split('-');
var b = Enumerable.Range(int.Parse((_itemsB[0])), (int.Parse(_itemsB[1]) - int.Parse((_itemsB[0])) + 1)).ToList();
var c = a.Except < int > (b).ToList();
/// different things tried here, but they are not good
res[count] = c.Min().ToString() + "-" + c.Max().ToString();
count++;
}
}
return res;
Any input will be of great help
You can use the Built-in SortedSet<T> collection to do most of the work for you like this:
The SortedSet<T> collection implements the useful UnionWith and ExceptWith methods which at least makes the code quite easy to follow:
private void button1_Click(object sender, EventArgs e)
{
string[] includeRanges = _string.Text.Replace(" ", "").Split(',');
string[] excludeRanges = _stringB.Text.Replace(" ", "").Split(',');
string[] includeRange, excludeRange;
SortedSet<int> includeSet = new SortedSet<int>();
SortedSet<int> excludeSet = new SortedSet<int>();
// Create a UNION of all the include ranges
foreach (string item in includeRanges)
{
includeRange = item.Split('-');
includeSet.UnionWith(Enumerable.Range(int.Parse(includeRange[0]), (int.Parse(includeRange[1]) - int.Parse(includeRange[0]) + 1)).ToList());
}
// Create a UNION of all the exclude ranges
foreach (string item in excludeRanges)
{
excludeRange = item.Split('-');
excludeSet.UnionWith(Enumerable.Range(int.Parse(excludeRange[0]), (int.Parse(excludeRange[1]) - int.Parse(excludeRange[0]) + 1)).ToList());
}
// Exclude the excludeSet from the includeSet
includeSet.ExceptWith(excludeSet);
//Format the output using a stringbuilder
StringBuilder sb = new StringBuilder();
int lastValue = -1;
foreach (int included in includeSet)
{
if (lastValue == -1)
{
sb.Append(included + "-");
lastValue = included;
}
else
{
if (lastValue == included - 1)
{
lastValue = included;
}
else
{
sb.Append(lastValue + ",");
sb.Append(included + "-");
lastValue = included;
}
}
}
sb.Append(lastValue);
result.Text = sb.ToString();
}
This should work faster than SortedSet trick, at least for large intervals. Idea is like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Test
{
using Pair = Tuple<int, int>; //for brevity
struct Point //point of an interval
{
public enum Border { Left, Right };
public enum Interval { Including, Excluding };
public int Val;
public int Brdr;
public int Intr;
public Point(int value, Border border, Interval interval)
{
Val = value;
Brdr = (border == Border.Left) ? 1 : -1;
Intr = (int)interval;
}
public override string ToString() =>
(Brdr == 1 ? "L" : "R") + (Intr == 0 ? "+ " : "- ") + Val;
}
class Program
{
static IEnumerable<Pair> GetInterval(string strIn, string strEx)
{
//a func to get interval border points from string:
Func<string, Point.Interval, IEnumerable<Point>> parse = (str, intr) =>
Regex.Matches(str, "[0-9]+").Cast<Match>().Select((s, idx) =>
new Point(int.Parse(s.Value), (Point.Border)(idx % 2), intr));
var INs = parse(strIn, Point.Interval.Including);
var EXs = parse(strEx, Point.Interval.Excluding);
var intrs = new int[2]; //current interval border control IN[0], EX[1]
int start = 0; //left border of a new resulting interval
//put all points in a line and loop:
foreach (var p in INs.Union(EXs).OrderBy(x => x.Val))
{
//check for start (close) of a new (cur) interval:
var change = (intrs[p.Intr] == 0) ^ (intrs[p.Intr] + p.Brdr == 0);
intrs[p.Intr] += p.Brdr;
if (!change) continue;
var In = p.Intr == 0 && intrs[1] == 0; //w no Ex
var Ex = p.Intr == 1 && intrs[0] > 0; //breaks In
var Open = intrs[p.Intr] > 0;
var Close = !Open;
if (In && Open || Ex && Close)
{
start = p.Val + p.Intr; //exclude point if Ex
}
else if (In && Close || Ex && Open)
{
yield return new Pair(start, p.Val - p.Intr);
}
}
}
static void Main(string[] args)
{
var strIN = "10-100, 200-300, 400-500, 420-480";
var strEX = "95-205, 410-420";
foreach (var i in GetInterval(strIN, strEX))
Console.WriteLine(i.Item1 + "-" + i.Item2);
Console.ReadLine();
}
}
}
So, you task could be separated to the list of subtasks:
Parse a source line of intervals to the list of objects
Concatinate intervals if they cross each over
Excludes intervals 'excludes' from 'includes'
I published my result code here: http://rextester.com/OBXQ56769
The code could be optimized as well, but I wanted it to be quite simple. Hope it will help you.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace ConsoleApplication
{
public class Program
{
private const string Includes = "10-100, 200-300, 400-500 ";
private const string Excludes = "95-205, 410-420";
private const string Pattern = #"(\d*)-(\d*)";
public static void Main(string[] args)
{
var includes = ParseIntevals(Includes);
var excludes = ParseIntevals(Excludes);
includes = ConcatinateIntervals(includes);
excludes = ConcatinateIntervals(excludes);
// The Result
var result = ExcludeFromInclude(includes, excludes);
foreach (var interval in result)
{
Console.WriteLine(interval.Min + "-" + interval.Max);
}
}
/// <summary>
/// Excludes intervals 'excludes' from 'includes'
/// </summary>
public static List<Interval> ExcludeFromInclude(List<Interval> includes, List<Interval> excludes)
{
var result = new List<Interval>();
if (!excludes.Any())
{
return includes.Select(x => x.Clone()).ToList();
}
for (int i = 0; i < includes.Count; i++)
{
for (int j = 0; j < excludes.Count; j++)
{
if (includes[i].Max < excludes[j].Min || includes[i].Min > excludes[j].Max)
continue; // no crossing
//1 Example: includes[i]=(10-20) excludes[j]=(15-25)
if (includes[i].Min < excludes[j].Min && includes[i].Max <= excludes[j].Max)
{
var interval = new Interval(includes[i].Min, excludes[j].Min - 1);
result.Add(interval);
break;
}
//2 Example: includes[i]=(10-25) excludes[j]=(15-20)
if (includes[i].Min <= excludes[j].Min && includes[i].Max >= excludes[j].Max)
{
if (includes[i].Min < excludes[j].Min)
{
var interval1 = new Interval(includes[i].Min, excludes[j].Min - 1);
result.Add(interval1);
}
if (includes[i].Max > excludes[j].Max)
{
var interval2 = new Interval(excludes[j].Max + 1, includes[i].Max);
result.Add(interval2);
}
break;
}
//3 Example: includes[i]=(15-25) excludes[j]=(10-20)
if (includes[i].Min < excludes[j].Max && includes[i].Max > excludes[j].Max)
{
var interval = new Interval(excludes[j].Max + 1, includes[i].Max);
result.Add(interval);
break;
}
}
}
return result;
}
/// <summary>
/// Concatinates intervals if they cross each over
/// </summary>
public static List<Interval> ConcatinateIntervals(List<Interval> intervals)
{
var result = new List<Interval>();
for (int i = 0; i < intervals.Count; i++)
{
for (int j = 0; j < intervals.Count; j++)
{
if (i == j)
continue;
if (intervals[i].Max < intervals[j].Min || intervals[i].Min > intervals[j].Max)
{
Interval interval = intervals[i].Clone();
result.Add(interval);
continue; // no crossing
}
//1
if (intervals[i].Min < intervals[j].Min && intervals[i].Max < intervals[j].Max)
{
var interval = new Interval(intervals[i].Min, intervals[j].Max);
result.Add(interval);
break;
}
//2
if (intervals[i].Min < intervals[j].Min && intervals[i].Max > intervals[j].Max)
{
Interval interval = intervals[i].Clone();
result.Add(interval);
break;
}
//3
if (intervals[i].Min < intervals[j].Max && intervals[i].Max > intervals[j].Max)
{
var interval = new Interval(intervals[j].Min, intervals[i].Max);
result.Add(interval);
break;
}
//4
if (intervals[i].Min > intervals[j].Min && intervals[i].Max < intervals[j].Max)
{
var interval = new Interval(intervals[j].Min, intervals[j].Max);
result.Add(interval);
break;
}
}
}
return result.Distinct().ToList();
}
/// <summary>
/// Parses a source line of intervals to the list of objects
/// </summary>
public static List<Interval> ParseIntevals(string intervals)
{
var matches = Regex.Matches(intervals, Pattern, RegexOptions.IgnoreCase);
var list = new List<Interval>();
foreach (Match match in matches)
{
var min = int.Parse(match.Groups[1].Value);
var max = int.Parse(match.Groups[2].Value);
list.Add(new Interval(min, max));
}
return list.OrderBy(x => x.Min).ToList();
}
/// <summary>
/// Interval
/// </summary>
public class Interval
{
public int Min { get; set; }
public int Max { get; set; }
public Interval()
{
}
public Interval(int min, int max)
{
Min = min;
Max = max;
}
public override bool Equals(object obj)
{
var obj2 = obj as Interval;
if (obj2 == null) return false;
return obj2.Min == Min && obj2.Max == Max;
}
public override int GetHashCode()
{
return this.ToString().GetHashCode();
}
public override string ToString()
{
return string.Format("{0}-{1}", Min, Max);
}
public Interval Clone()
{
return (Interval) this.MemberwiseClone();
}
}
}
}
Lots of ways to solve this. The LINQ approach hasn't been discussed yet - this is how I would do it:
// declaring a lambda fn because it's gonna be used by both include/exclude
// list
Func<string, IEnumerable<int>> rangeFn =
baseInput =>
{
return baseInput.Split (new []{ ',', ' ' },
StringSplitOptions.RemoveEmptyEntries)
.SelectMany (rng =>
{
var range = rng.Split (new []{ '-' },
StringSplitOptions.RemoveEmptyEntries)
.Select(i => Convert.ToInt32(i));
// just in case someone types in
// a reverse range (e.g. 10-5), LOL...
var start = range.Min ();
var end = range.Max ();
return Enumerable.Range (start, (end - start + 1));
});
};
var includes = rangeFn (_string);
var excludes = rangeFn (_stringB);
var result = includes.Except (excludes).Distinct().OrderBy(r => r);

Program to find minimum number in string

I have a c# class like so
internal class QueuedMinimumNumberFinder : ConcurrentQueue<int>
{
private readonly string _minString;
public QueuedMinimumNumberFinder(string number, int takeOutAmount)
{
if (number.Length < takeOutAmount)
{
throw new Exception("Error *");
}
var queueIndex = 0;
var queueAmount = number.Length - takeOutAmount;
var numQueue = new ConcurrentQueue<int>(number.ToCharArray().Where(m => (int) Char.GetNumericValue(m) != 0).Select(m=>(int)Char.GetNumericValue(m)).OrderBy(m=>m));
var zeroes = number.Length - numQueue.Count;
while (queueIndex < queueAmount)
{
int next;
if (queueIndex == 0)
{
numQueue.TryDequeue(out next);
Enqueue(next);
} else
{
if (zeroes > 0)
{
Enqueue(0);
zeroes--;
} else
{
numQueue.TryDequeue(out next);
Enqueue(next);
}
}
queueIndex++;
}
var builder = new StringBuilder();
while (Count > 0)
{
int next = 0;
TryDequeue(out next);
builder.Append(next.ToString());
}
_minString = builder.ToString();
}
public override string ToString() { return _minString; }
}
The point of the program is to find the minimum possible integer that can be made by taking out any x amount of characters from a string(example 100023 is string, if you take out any 3 letters, the minimum int created would be 100). My question is, is this the correct way to do this? Is there a better data structure that can be used for this problem?
First Edit:
Here's how it looks now
internal class QueuedMinimumNumberFinder
{
private readonly string _minString;
public QueuedMinimumNumberFinder(string number, int takeOutAmount)
{
var queue = new Queue<int>();
if (number.Length < takeOutAmount)
{
throw new Exception("Error *");
}
var queueIndex = 0;
var queueAmount = number.Length - takeOutAmount;
var numQueue = new List<int>(number.Where(m=>(int)Char.GetNumericValue(m)!=0).Select(m=>(int)Char.GetNumericValue(m))).ToList();
var zeroes = number.Length - numQueue.Count;
while (queueIndex < queueAmount)
{
if (queueIndex == 0)
{
var nextMin = numQueue.Min();
numQueue.Remove(nextMin);
queue.Enqueue(nextMin);
} else
{
if (zeroes > 1)
{
queue.Enqueue(0);
zeroes--;
} else
{
var nextMin = numQueue.Min();
numQueue.Remove(nextMin);
queue.Enqueue(nextMin);
}
}
queueIndex++;
}
var builder = new StringBuilder();
while (queue.Count > 0)
{
builder.Append(queue.Dequeue().ToString());
}
_minString = builder.ToString();
}
public override string ToString() { return _minString; }
}
A pretty simple and efficient implementation can be made, once you realize that your input string digits map to the domain of only 10 possible values: '0' .. '9'.
This can be encoded as the number of occurrences of a specific digit in your input string using a simple array of 10 integers: var digit_count = new int[10];
#MasterGillBates describes this idea in his answer.
You can then regard this array as your priority queue from which you can dequeue the characters you need by iteratively removing the lowest available character (decreasing its occurrence count in the array).
The code sample below provides an example implementation for this idea.
public static class MinNumberSolver
{
public static string GetMinString(string number, int takeOutAmount)
{
// "Add" the string by simply counting digit occurrance frequency.
var digit_count = new int[10];
foreach (var c in number)
if (char.IsDigit(c))
digit_count[c - '0']++;
// Now remove them one by one in lowest to highest order.
// For the first character we skip any potential leading 0s
var selected = new char[takeOutAmount];
var start_index = 1;
selected[0] = TakeLowest(digit_count, ref start_index);
// For the rest we start in digit order at '0' first.
start_index = 0;
for (var i = 0; i < takeOutAmount - 1; i++)
selected[1 + i] = TakeLowest(digit_count, ref start_index);
// And return the result.
return new string(selected);
}
private static char TakeLowest(int[] digit_count, ref int start_index)
{
for (var i = start_index; i < digit_count.Length; i++)
{
if (digit_count[i] > 0)
{
start_index = ((--digit_count[i] > 0) ? i : i + 1);
return (char)('0' + i);
}
}
throw new InvalidDataException("Input string does not have sufficient digits");
}
}
Just keep a count of how many times each digit appears. An array of size 10 will do. Count[i] gives the count of digit i.
Then pick the smallest non-zero i first, then pick the smallest etc and form your number.
Here's my solution using LINQ:
public string MinimumNumberFinder(string number, int takeOutAmount)
{
var ordered = number.OrderBy(n => n);
var nonZero = ordered.SkipWhile(n => n == '0');
var zero = ordered.TakeWhile(n => n == '0');
var result = nonZero.Take(1)
.Concat(zero)
.Concat(nonZero.Skip(1))
.Take(number.Length - takeOutAmount);
return new string(result.ToArray());
}
You could place every integer into a list and find all possible sequences of these values. From the list of sequences, you could sort through taking only the sets which have the number of integers you want. From there, you can write a quick function which parses a sequence into an integer. Next, you could store all of your parsed sequences into an array or an other data structure and sort based on value, which will allow you to select the minimum number from the data structure. There may be simpler ways to do this, but this will definitely work and gives you options as far as how many digits you want your number to have.
If I'm understanding this correctly, why don't you just pick out your numbers starting with the smallest number greater than zero. Then pick out all zeroes, then any remaining number if all the zeroes are picked up. This is all depending on the length of your ending result
In your example you have a 6 digit number and you want to pick out 3 digits. This means you'll only have 3 digits left. If it was a 10 digit number, then you would end up with a 7 digit number, etc...
So have an algorithm that knows the length of your starting number, how many digits you plan on removing, and the length of your ending number. Then just pick out the numbers.
This is just quick and dirty code:
string startingNumber = "9999903040404"; // "100023";
int numberOfCharactersToRemove = 3;
string endingNumber = string.Empty;
int endingNumberLength = startingNumber.Length - numberOfCharactersToRemove;
while (endingNumber.Length < endingNumberLength)
{
if (string.IsNullOrEmpty(endingNumber))
{
// Find the smallest digit in the starting number
for (int i = 1; i <= 9; i++)
{
if (startingNumber.Contains(i.ToString()))
{
endingNumber += i.ToString();
startingNumber = startingNumber.Remove(startingNumber.IndexOf(i.ToString()), 1);
break;
}
}
}
else if (startingNumber.Contains("0"))
{
// Add any zeroes
endingNumber += "0";
startingNumber = startingNumber.Remove(startingNumber.IndexOf("0"), 1);
}
else
{
// Add any remaining numbers from least to greatest
for (int i = 1; i <= 9; i++)
{
if (startingNumber.Contains(i.ToString()))
{
endingNumber += i.ToString();
startingNumber = startingNumber.Remove(startingNumber.IndexOf(i.ToString()), 1);
break;
}
}
}
}
Console.WriteLine(endingNumber);
100023 starting number resulted in 100 being the end result
9999903040404 starting number resulted in 3000044499 being the end result
Here's my version to fix this problem:
DESIGN:
You can sort your list using a binary tree , there are a lot of
implementations , I picked this one
Then you can keep track of the number of the Zeros you have in your
string Finally you will end up with two lists: I named one
SortedDigitsList and the other one ZeroDigitsList
perform a switch case to determine which last 3 digits should be
returned
Here's the complete code:
class MainProgram2
{
static void Main()
{
Tree theTree = new Tree();
Console.WriteLine("Please Enter the string you want to process:");
string input = Console.ReadLine();
foreach (char c in input)
{
// Check if it's a digit or not
if (c >= '0' && c <= '9')
{
theTree.Insert((int)Char.GetNumericValue(c));
}
}
//End of for each (char c in input)
Console.WriteLine("Inorder traversal resulting Tree Sort without the zeros");
theTree.Inorder(theTree.ReturnRoot());
Console.WriteLine(" ");
//Format the output depending on how many zeros you have
Console.WriteLine("The final 3 digits are");
switch (theTree.ZeroDigitsList.Count)
{
case 0:
{
Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], theTree.SortedDigitsList[1], theTree.SortedDigitsList[2]);
break;
}
case 1:
{
Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], 0, theTree.SortedDigitsList[2]);
break;
}
default:
{
Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], 0, 0);
break;
}
}
Console.ReadLine();
}
}//End of main()
}
class Node
{
public int item;
public Node leftChild;
public Node rightChild;
public void displayNode()
{
Console.Write("[");
Console.Write(item);
Console.Write("]");
}
}
class Tree
{
public List<int> SortedDigitsList { get; set; }
public List<int> ZeroDigitsList { get; set; }
public Node root;
public Tree()
{
root = null;
SortedDigitsList = new List<int>();
ZeroDigitsList = new List<int>();
}
public Node ReturnRoot()
{
return root;
}
public void Insert(int id)
{
Node newNode = new Node();
newNode.item = id;
if (root == null)
root = newNode;
else
{
Node current = root;
Node parent;
while (true)
{
parent = current;
if (id < current.item)
{
current = current.leftChild;
if (current == null)
{
parent.leftChild = newNode;
return;
}
}
else
{
current = current.rightChild;
if (current == null)
{
parent.rightChild = newNode;
return;
}
}
}
}
}
//public void Preorder(Node Root)
//{
// if (Root != null)
// {
// Console.Write(Root.item + " ");
// Preorder(Root.leftChild);
// Preorder(Root.rightChild);
// }
//}
public void Inorder(Node Root)
{
if (Root != null)
{
Inorder(Root.leftChild);
if (Root.item > 0)
{
SortedDigitsList.Add(Root.item);
Console.Write(Root.item + " ");
}
else
{
ZeroDigitsList.Add(Root.item);
}
Inorder(Root.rightChild);
}
}

Postfix increment into if, c#

Code example:
using System;
public class Test {
public static void Main() {
int a = 0;
if(a++ == 0){
Console.WriteLine(a);
}
}
}
In this code the Console will write: 1. I can write this code in another way:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
}
These two examples work exactly the same (from what I know about postfix).
The problem is with this example coming from the Microsoft tutorials:
using System;
public class Document {
// Class allowing to view the document as an array of words:
public class WordCollection {
readonly Document document;
internal WordCollection (Document d){
document = d;
}
// Helper function -- search character array "text", starting
// at character "begin", for word number "wordCount". Returns
//false if there are less than wordCount words. Sets "start" and
//length to the position and length of the word within text
private bool GetWord(char[] text, int begin, int wordCount,
out int start, out int length) {
int end = text.Length;
int count = 0;
int inWord = -1;
start = length = 0;
for (int i = begin; i <= end; ++i){
bool isLetter = i < end && Char.IsLetterOrDigit(text[i]);
if (inWord >= 0) {
if (!isLetter) {
if (count++ == wordCount) {//PROBLEM IS HERE!!!!!!!!!!!!
start = inWord;
length = i - inWord;
return true;
}
inWord = -1;
}
} else {
if (isLetter) {
inWord = i;
}
}
}
return false;
}
//Indexer to get and set words of the containing document:
public string this[int index] {
get
{
int start, length;
if(GetWord(document.TextArray, 0, index, out start,
out length)) {
return new string(document.TextArray, start, length);
} else {
throw new IndexOutOfRangeException();
}
}
set {
int start, length;
if(GetWord(document.TextArray, 0, index, out start,
out length))
{
//Replace the word at start/length with
// the string "value"
if(length == value.Length){
Array.Copy(value.ToCharArray(), 0,
document.TextArray, start, length);
}
else {
char[] newText = new char[document.TextArray.Length +
value.Length - length];
Array.Copy(document.TextArray, 0, newText, 0, start);
Array.Copy(value.ToCharArray(), 0, newText, start, value.Length);
Array.Copy(document.TextArray, start + length, newText,
start + value.Length, document.TextArray.Length - start - length);
document.TextArray = newText;
}
} else {
throw new IndexOutOfRangeException();
}
}
}
public int Count {
get {
int count = 0, start = 0, length = 0;
while (GetWord(document.TextArray, start + length,
0, out start, out length)) {
++count;
}
return count;
}
}
}
// Class allowing the document to be viewed like an array
// of character
public class CharacterCollection {
readonly Document document;
internal CharacterCollection(Document d) {
document = d;
}
//Indexer to get and set character in the containing
//document
public char this[int index] {
get {
return document.TextArray[index];
}
set {
document.TextArray[index] = value;
}
}
//get the count of character in the containing document
public int Count {
get {
return document.TextArray.Length;
}
}
}
//Because the types of the fields have indexers,
//these fields appear as "indexed properties":
public WordCollection Words;
public readonly CharacterCollection Characters;
private char[] TextArray;
public Document(string initialText) {
TextArray = initialText.ToCharArray();
Words = new WordCollection(this);
Characters = new CharacterCollection(this);
}
public string Text {
get {
return new string(TextArray);
}
}
class Test {
static void Main() {
Document d = new Document(
"peter piper picked a peck of pickled peppers. How many pickled peppers did peter piper pick?"
);
//Change word "peter" to "penelope"
for(int i = 0; i < d.Words.Count; ++i){
if (d.Words[i] == "peter") {
d.Words[i] = "penelope";
}
}
for (int i = 0; i < d.Characters.Count; ++i) {
if (d.Characters[i] == 'p') {
d.Characters[i] = 'P';
}
}
Console.WriteLine(d.Text);
}
}
}
If I change the code marked above to this:
if (count == wordCount) {//PROBLEM IS HERE
start = inWord;
length = i - inWord;
count++;
return true;
}
I get an IndexOutOfRangeException, but I don't know why.
Your initial assumption is incorrect (that the two examples work exactly the same). In the following version, count is incremented regardless of whether or not it is equal to wordCount:
if (count++ == wordCount)
{
// Code omitted
}
In this version, count is ONLY incremented when it is equal to wordCount
if (count == wordCount)
{
// Other code omitted
count++;
}
EDIT
The reason this is causing you a failure is that, when you are searching for the second word (when wordCount is 1), the variable count will never equal wordCount (because it never gets incremented), and therefore the GetWord method returns false, which then triggers the else clause in your get method, which throws an IndexOutOfRangeException.
In your version of the code, count is only being incremented when count == wordCount; in the Microsoft version, it's being incremented whether the condition is met or not.
using System;
public class Test {
public static void Main() {
int a = 0;
if(a++ == 0){
Console.WriteLine(a);
}
}
}
Is not quite the same as:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
}
In the second case a++ is executed only if a == 0. In the first case a++ is executed every time we check the condition.
There is your mistake:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
}
It should be like this:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
else
a++;
}
a gets alwasy increased. This means, that in your code example count will get only increased when count == wordCount (In which case the method will return true anyway...). You basicly never increasing count.

Categories