Count consequitive numbers in a string C# - c#

I have a string {101110111010001111} I'm searching for the total number of all sequences of equal bits with an exact length of 3 bits. In above string the answer willbe 3 (please note that the last one "1111" doesn't count as it has more than 3 equal bits.
Any suggestions how to do this?

If you don't want a simple solution, try this:
string s = "1101110111010001111";
var regex = new Regex(#"(.)\1+");
var matches = regex.Matches(s);
int count = matches.Cast<Match>().Where(x => x.Length == 3).Count();
Explanation:
The regex finds sets of 2 or more identical characters (not limited to 0's and 1's)
Then only sets of exactly 3 characters are counted

Is it that you need? Sometimes the simplest solutions are the best:
public static int Count(string txt)
{
// TODO validation etc
var result = 0;
char lstChr = txt[0];
int lastChrCnt = 1;
for (var i = 1; i < txt.Length; i++)
{
if (txt[i] == lstChr)
{
lastChrCnt += 1;
}
else
{
if (lastChrCnt == 3)
{
result += 1;
}
lstChr = txt[i];
lastChrCnt = 1;
}
}
return lastChrCnt == 3 ? result + 1 : result;
}

You can use a regular expression:
Regex.Matches(s, #"((?<=0)|^)111((?=0)|$)|((?<=1)|^)000((?=1)|$)");
Here's the same expression with comments:
Regex.Matches(s, #"
(
(?<=0) # is preceeded by a 0
| # or
^ # is at start
)
111 # 3 1's
(
(?=0) # is followed by a 0
| # or
$ # is at start
)
| # - or -
(
(?<=1) # is preceeded by a 1
| # or
^ # is at start
)
000 # 3 0's
(
(?=1) # followed by a 1
| # or
$ # is at end
)", RegexOptions.IgnorePatternWhitespace).Dump();

You can split the string by "111" to get an array. You can then simply count the lines that not begins with "1" with the linq.
See the sample:
using System;
using System.Linq;
namespace experiment
{
class Program
{
static void Main(string[] args)
{
string data = "{101110111010001111}";
string[] sequences = data.Split(new string[] {"111"}, StringSplitOptions.None);
int validCounts = sequences.Count(i => i.Substring(0, 1) != "1");
Console.WriteLine("Count of sequences: {0}", validCounts);
// See the splited array
foreach (string item in sequences) {
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}

Yet another approach: Since you are only counting bits, you can split the string by "1" and "0" and then count all elements with lenght 3:
string inputstring = "101110111010001111";
string[] str1 = inputstring.Split('0');
string[] str2 = inputstring.Split('1');
int result = str1.Where(s => s.Length == 3).Count() + str2.Where(s => s.Length == 3).Count();
return result

An algorithmic solution. Checks for any n consecutive characters. But this is not completely tested for all negative scenarios.
public static int GetConsecutiveCharacterCount(this string input, int n)
{
// Does not contain expected number of characters
if (input.Length < n || n < 1)
return 0;
return Enumerable.Range(0, input.Length - (n - 1)) // Last n-1 characters will be covered in the last but one iteration.
.Where(x => Enumerable.Range(x, n).All(y => input[x] == input[y]) && // Check whether n consecutive characters match
((x - 1) > -1 ? input[x] != input[x - 1] : true) && // Compare the previous character where applicable
((x + n) < input.Length ? input[x] != input[x + n] : true) // Compare the next character where applicable
)
.Count();
}

Related

Shortening a string of numbers

I have the following sequence of numbers:
You can see that those numbers a lot. I want to shorten that string. Let's say if the string contains more than 20 numbers, it should display 18 numbers, then "..." and then the last two of the sequence.
I could probably do that by adding those numbers in a List<int> or HashSet<int> (HashSet might be faster in this case), but I think it will be slow.
StringBuilder temp = new StringBuilder();
for (...)
{
temp.Append($"{number} ");
}
var sequence = temp.ToString();
Example of what I want:
7 9 12 16 18 21 25 27 30 34 36 39 43 45 48 52 54 57 ... 952 954
Note that I want only fast ways.
This version is about 8 times faster than the other answers and allocates only about 6% as much memory. I think you'll be hard-pressed to find a faster version:
static string Truncated(string input)
{
var indexOfEighteenthSpace = IndexOfCharSeekFromStart(input, ' ', 18);
if (indexOfEighteenthSpace <= 0) return input;
var indexOfSecondLastSpace = IndexOfCharSeekFromEnd(input, ' ', 2);
if (indexOfSecondLastSpace <= 0) return input;
if (indexOfSecondLastSpace <= indexOfEighteenthSpace) return input;
var leadingSegment = input.AsSpan().Slice(0, indexOfEighteenthSpace);
var trailingSegment = input.AsSpan().Slice(indexOfSecondLastSpace + 1);
return string.Concat(leadingSegment, " ... ", trailingSegment);
static int IndexOfCharSeekFromStart(string input, char value, int count)
{
var startIndex = 0;
for (var i = 0; i < count; i++)
{
startIndex = input.IndexOf(value, startIndex + 1);
if (startIndex <= 0) return startIndex;
}
return startIndex;
}
static int IndexOfCharSeekFromEnd(string input, char value, int count)
{
var endIndex = input.Length - 1;
for (var i = 0; i < count; i++)
{
endIndex = input.LastIndexOf(value, endIndex - 1);
if (endIndex <= 0) return endIndex;
}
return endIndex;
}
}
Small individual steps
How do I make a list from this sequence (string)?
var myList = myOriginalSequence.Split(' ').ToList();
How do you take the first 18 numbers from a list?
var first18Numbers = myList.Take(18);
How do you take the last 2 numbers from a list?
var last2Numbers = myList.Skip(myList.Count() - 2);
How do you ensure that this is only done when there are more than 20 numbers in the list?
if(myList.Count() > 20)
How do you make a new sequence string from a list?
var myNewSequence = String.Join(" ", myList);
Putting it all together
var myList = myOriginalSequence.Split(' ').ToList();
string myNewSequence;
if(myList.Count() > 20)
{
var first18Numbers = myList.Take(18);
var first18NumbersString = String.Join(" ", first18Numbers);
var last2Numbers = myList.Skip(myList.Count() - 2);
var last2NumbersString = String.Join(" ", last2Numbers);
myNewSequence = $"{first18NumbersString} ... {last2NumbersString}"
}
else
{
myNewSequence = myOriginalSequence;
}
Console.WriteLine(myNewSequence);
Try this:
public string Shorten(string str, int startCount, int endCount)
{
//first remove any leading or trailing whitespace
str = str.Trim();
//find the first startCount numbers by using IndexOf space
//i.e. this counts the number of spaces from the start until startCount is achieved
int spaceCount = 1;
int startInd = str.IndexOf(' ');
while (spaceCount < startCount && startInd > -1)
{
startInd = str.IndexOf(' ',startInd +1);
spaceCount++;
}
//find the last endCount numbers by using LastIndexOf space
//i.e. this counts the number of spaces from the end until endCount is achieved
int lastSpaceCount = 1;
int lastInd = str.LastIndexOf(' ');
while (lastSpaceCount < endCount && lastInd > -1)
{
lastInd = str.LastIndexOf(' ', lastInd - 1);
lastSpaceCount++;
}
//if the start ind or end ind are -1 or if lastInd <= startIndjust return the str
//as its not long enough and so doesn't need shortening
if (startInd == -1 || lastInd == -1 || lastInd <= startInd) return str;
//otherwise return the required shortened string
return $"{str.Substring(0, startInd)} ... {str.Substring(lastInd + 1)}";
}
the output of this:
Console.WriteLine(Shorten("123 123 123 123 123 123 123 123 123 123 123",4,3));
is:
123 123 123 123 ... 123 123 123
I think this may help :
public IEnumerable<string> ShortenList(string input)
{
List<int> list = input.Split(" ").Select(x=>int.Parse(x)).ToList();
if (list.Count > 20)
{
List<string> trimmedStringList = list.Take(18).Select(x=>x.ToString()).ToList();
trimmedStringList.Add("...");
trimmedStringList.Add(list[list.Count-2].ToString());
trimmedStringList.Add(list[list.Count - 1].ToString());
return trimmedStringList;
}
return list.Select(x => x.ToString());
}
No idea what the speed on this would be like but as a wild suggestion, you said the numbers come in string format and it looks like they're seperated by spaces. You could get the index of the 19th space (to display 18 numbers) using any of the methods found here, and substring from index 0 to that index and concatenate 3 dots. Something like this:
numberListString.SubString(0, IndexOfNth(numberListString, ' ', 19)) + "..."
(Not accurate code, adding or subtracting indexes or adjusting values (19) may be required).
EDIT: Just saw that after the dots you wanted to have the last 2 numbers, you can use the same technique! Just concatenate that result again.
NOTE: I used this whacky technique because the OP said they wanted fast ways, I'm just offering a potential option to benchmark :)
There is an alternative way that prevents iteration through the entire string of numbers and is reasonably fast.
Strings in .NET are basically an array of chars, and can be referenced on an individual basis using array referencing ([1..n]). This can be used to our advantage by simply testing for the correct number of spaces from the start and end respectively.
There are no niceties in the code, but they could be optimised later (for instance, by ensuring that there's actually something in the string, that the string is trimmed etc.).
The functions below could also be optimised to a single function if you're feeling energetic.
string finalNumbers = GetStartNumbers(myListOfNumbers, 18);
if(finalNumbers.EndsWith(" ... "))
finalNumbers += GetEndNumbers(myListOfNumbers, 2);
public string GetStartNumbers(string listOfNumbers, int collectThisManyNumbers)
{
int spaceCounter = 0; // The current count of spaces
int charPointer = 0; // The current character in the string
// Loop through the list of numbers until we either run out of characters
// or get to the appropriate 'space' position...
while(spaceCounter < collectThisManyNumbers && charPointer <= listOfNumbers.Length)
{
// The following line will add 1 to spaceCounter if the character at the
// charPointer position is a space. The charPointer is then incremented...
spaceCounter += ( listOfNumbers[charPointer++]==' ' ? 1 : 0 );
}
// Now return our value based on the last value of charPointer. Note that
// if our string doesn't have the right number of elements, then it will
// not be suffixed with ' ... '
if(spaceCounter < collectThisManyNumbers)
return listOfNumbers.Substring(0, charPointer - 1);
else
return listOfNumbers.Substring(0, charPointer - 1) + " ... ";
}
public string GetEndNumbers(string listOfNumbers, int collectThisManyNumbers)
{
int spaceCounter = 0; // The current count of spaces
int charPointer = listOfNumbers.Length; // The current character in the string
// Loop through the list of numbers until we either run out of characters
// or get to the appropriate 'space' position...
while(spaceCounter < collectThisManyNumbers && charPointer >= 0)
{
// The following line will add 1 to spaceCounter if the character at the
// charPointer position is a space. The charPointer is then decremented...
spaceCounter += ( listOfNumbers[charPointer--]==' ' ? 1 : 0 );
}
// Now return our value based on the last value of charPointer...
return listOfNumbers.Substring(charPointer);
}
Some people find the use of ++ and -- objectionable but it's up to you. If you want to do the maths and logic, knock yourself out!
Please note that this code is quite long because it's commented to the far end of a fart.

Count the number of contiguous equal characters

I need to validate Serial numbers and one of the rules is that there are up to 5 contiguous equal characters allowed.
Example valid:
012W212222123 // 4x the digit 2 contiguous
Example invalid:
012W764444443 // 6x the digit 4
So I tried to get the maximum number of contiguous characters without success
int maxCount = "012W764444443".GroupBy(x => x).Max().Count();
I suggest using a regex for a check to see if there are 5 or more consecutive digits:
Regex.IsMatch(input, #"^(?!.*([0-9])\1{4})")
If any characters are meant:
Regex.IsMatch(input, #"^(?!.*(.)\1{4})")
See the regex demo
The regex finds a match in a string that contains less than 5 identical consecutive digits (version with [0-9]) or any characters other than a newline (version with .).
Details:
^ - start of string
-(?!.*(.)\1{4}) - a negative lookahead that fails the match if the pattern is matched:
.* - any 0+ chars other than a newline
(.) - Group 1 capturing any char but a newline
\1{4} - exactly 4 consecutive occurrences of the same value stored inside Group 1 (where \1 is a backreference and the {4} is a range/bound/limiting quantifier).
C#:
var strs = new List<string> { "012W212222123", "012W764444443"};
foreach (var s in strs)
Console.WriteLine("{0}: {1}", s, Regex.IsMatch(s, #"^(?!.*(.)\1{4})"));
Yet another option is to use this function:
public static int MaxNumberOfConsecutiveCharacters(string s)
{
if (s == null) throw new ArgumentNullException(nameof(s));
if (s.Length == 0) return 0;
int maxCount = 1;
int count = 1;
for (int i = 1; i < s.Length; i++)
{
if (s[i] == s[i-1])
{
count++;
if (count > maxCount) maxCount = count;
}
else
{
count = 1;
}
}
return maxCount;
}
Obviously, this is a lot more code than a regular expression. Depending on your knowledge of regular expressions, this may or may not be more readable to you. Also, this is probably more efficient than using a regular expression, which may or may not be important to you.
It's a little inefficient, but this works:
var max =
"012W212222123"
.Aggregate(
new { Char = ' ', Count = 0, Max = 0 },
(a, c) =>
a.Char == c
? new { Char = c, Count = a.Count + 1, Max = a.Max > a.Count + 1 ? a.Max : a.Count + 1 }
: new { Char = c, Count = 1, Max = a.Max > 1 ? a.Max : 1 })
.Max;
I tried with both inputs and got the right number of maximum repeats each time.

Where is the flaw in my algorithm to get the largest palindrome is a string representation of a number?

I'm trying to get the largest palindrome that can be formed by k replacements of digits in the string number.
e.g.
number="3943",k=1 --> "3993"
For that exact test case I am getting "393" and for some test cases I am getting an error like
Unhandled Exception: System.InvalidOperationException: Sequence
contains no elements at System.Linq.Enumerable.Last[TSource]
(IEnumerable`1 source) <0x414ec920 + 0x001ab> in :0
at Solution.LargestPalindrome (System.String numstr, Int32 k)
[0x00197] in solution.cs:74 at
Solution+c__AnonStorey0.<>m__0 (System.String str)
[0x00009] in solution.cs:61
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Solution
{
static bool IsPalindrome(string s)
{
// returns true or false depending on whether the string
// s is a palindrome
// e.g. "abba" --> true, "acba" --> false
for(int i = 0, j = s.Length - 1; i < j; ++i, --j)
{
if(s[i] != s[j])
return false;
}
return true;
}
static string Replace(string s, int i, char c)
{
// returns a copy of s with the character at index i
// replaced by character c
// e.g. "george",2,"x" --> "gexrge"
string part1 = s.Length > 0 ? s.Substring(0, i) : string.Empty;
string part2 = i < (s.Length - 1) ? c.ToString() : string.Empty;
string part3 = (i + 1) < (s.Length - 1) ? s.Substring(i + 1, s.Length - i - 1) : string.Empty;
return part1 + part2 + part3;
}
static string LargestPalindrome(string numstr, int k)
{
// numstr: string representation of number
// k: maximum number of digit replacements allowed
// if no digit replacements allowed, return same string
if(k == 0)
return numstr;
// digrange will be {'0', '1', ..., '9'}
List<char> digrange = new List<char>();
for(char c = '0'; c <= '9'; ++c)
digrange.Add(c);
// possibilities will be all possibilities of replacing one digit from numstr
// e.g. numstr="02" --> possibilities={"12","22","32",...,"92","00","01","03","09"}
List<string> possibilities = new List<string>();
for(int i = 0; i < numstr.Length; ++i)
{
foreach(char dig in digrange.Where(d => d != numstr[i]))
{
possibilities.Add(Replace(numstr,i,dig));
}
}
// if k = 1, get all the strings in cumulativePossiblities that are palindromes;
// else, transform each into the largest palindrome formed by k - 1 character
// replacements of itself
var cumulativePossibilities = k == 1
? possibilities.Where(str => IsPalindrome(str))
: possibilities.Select(str => LargestPalindrome(str, k - 1)).Where(str => IsPalindrome(str));
// sort cumulativePossibilities in ascending order of the integer representation
// of the strings
cumulativePossibilities.ToList().Sort((s1,s2) => {
Int64 i1 = Int64.Parse(s1),
i2 = Int64.Parse(s2);
return (i1 > i2) ? 1 : ((i1 == i2) ? 0 : -1);
});
// get the last element of the now-sorted cumulativePossibilities,
// which will be the largest number represented by the possible strings
// or will be null if there are none
string largest = cumulativePossibilities.Last();
// return the largest or "-1" if there were none
return largest != null ? largest : "-1";
}
static void Main(String[] args)
{
string[] tokens_n = Console.ReadLine().Split(' ');
int k = Convert.ToInt32(tokens_n[1]);
string number = Console.ReadLine();
// use brute force algorithm to find largest palindrome of the string
// representation of the number after k replacements of characters
Console.WriteLine(LargestPalindrome(number,k));
}
}
Not very efficient method, but simple to implement; the key feature is using PalindromeSubstitutions (counting how many characters' substitutions prevents string from being palindrome) instead of IsPalindrome (just a fact if string is palindrome or not)
// How many characters should be substituted in order to
// turn the string into palindrom
private static int PalindromeSubstitutions(string value) {
if (string.IsNullOrEmpty(value))
return 0;
int result = 0;
for (int i = 0; i < value.Length / 2; ++i)
if (value[i] != value[value.Length - 1 - i])
result += 1;
return result;
}
// Let's test all substrings of size Length, Length - 1, ... , 2, 1
// until we find substring with required tolerance
private static string BestPalindromeSubstitutions(string value, int tolerance) {
for (int size = value.Length; size >= 1; --size)
for (int start = 0; start <= value.Length - size; ++start)
if (PalindromeSubstitutions(value.Substring(start, size)) <= tolerance)
return value.Substring(start, size);
return "";
}
private static string SubstituteToPalindrome(string value) {
if (string.IsNullOrEmpty(value))
return value;
StringBuilder sb = new StringBuilder(value);
for (int i = 0; i < value.Length / 2; ++i)
sb[value.Length - 1 - i] = sb[i];
return sb.ToString();
}
Test:
string input = "73943";
string best = BestPalindromeSubstitutions(input, 1);
string report =
string.Format("Best palindrome {0} -> {1}", best, SubstituteToPalindrome(best));
Output
Best palindrome 3943 -> 3993
The problem is a quite simple example of greedy algorithm. let's first count how many permutations are required (at minimum) to transform the number into palindrome.
int req = 0;
for(int i = 0; i <= (s.length()-1)/2; i++){
if (s[i] != s[s.length()-1-i] && i != s.length()-1-i) req++;
}
Now once it is done, let's go through digits from left to right once again: i goes through 0 to (s.length()-1)/2 inclusive. Consider the following cases ( here i is not the middle letter, that case we consider separately) :
s[i] == s[s.length()-i-1], it wasn't counted in req, so if k >= req + 2 and s[i] != '9', we change both letters to '9', and reduce k by 2, req remains unchanged. But note that we guaranteed that there are enough operations left to make sure that number can be turned into palindrome (if it was initially possible)
s[i] != s[s.length()-i-1] - now if k == req or one of the letters is '9', then do the following: s[i]=s[s.length()-i-1]=max({s[i], s[s.length()-i-1]}). Reduce both k and req by 1.
Now if k > req and both letters are not '9', we change them both to 9. k -= 2, req -= 1.
Now if i = s.length()-i-1 and k > 0, change this letter s[i] to '9'.
The result u get at the end is what u are looking for.
Total complexity is O(n).

String Combinations With Character Replacement

I am trying to work through a scenario I haven't seen before and am struggling to come up with an algorithm to implement this properly. Part of my problem is a hazy recollection of the proper terminology. I believe what I am needing is a variation of the standard "combination" problem, but I could well be off there.
The Scenario
Given an example string "100" (let's call it x), produce all combinations of x that swap out one of those 0 (zero) characters for a o (lower-case o). So, for the simple example of "100", I would expect this output:
"100"
"10o"
"1o0"
"1oo"
This would need to support varying length strings with varying numbers of 0 characters, but assume there would never be more than 5 instances of 0.
I have this very simple algorithm that works for my sample of "100" but falls apart for anything longer/more complicated:
public IEnumerable<string> Combinations(string input)
{
char[] buffer = new char[input.Length];
for(int i = 0; i != buffer.Length; ++i)
{
buffer[i] = input[i];
}
//return the original input
yield return new string(buffer);
//look for 0's and replace them
for(int i = 0; i != buffer.Length; ++i)
{
if (input[i] == '0')
{
buffer[i] = 'o';
yield return new string(buffer);
buffer[i] = '0';
}
}
//handle the replace-all scenario
yield return input.Replace("0", "o");
}
I have a nagging feeling that recursion could be my friend here, but I am struggling to figure out how to incorporate the conditional logic I need here.
Your guess was correct; recursion is your friend for this challenge. Here is a simple solution:
public static IEnumerable<string> Combinations(string input)
{
int firstZero = input.IndexOf('0'); // Get index of first '0'
if (firstZero == -1) // Base case: no further combinations
return new string[] { input };
string prefix = input.Substring(0, firstZero); // Substring preceding '0'
string suffix = input.Substring(firstZero + 1); // Substring succeeding '0'
// e.g. Suppose input was "fr0d00"
// Prefix is "fr"; suffix is "d00"
// Recursion: Generate all combinations of suffix
// e.g. "d00", "d0o", "do0", "doo"
var recursiveCombinations = Combinations(suffix);
// Return sequence in which each string is a concatenation of the
// prefix, either '0' or 'o', and one of the recursively-found suffixes
return
from chr in "0o" // char sequence equivalent to: new [] { '0', 'o' }
from recSuffix in recursiveCombinations
select prefix + chr + recSuffix;
}
This works for me:
public IEnumerable<string> Combinations(string input)
{
var head = input[0] == '0' //Do I have a `0`?
? new [] { "0", "o" } //If so output both `"0"` & `"o"`
: new [] { input[0].ToString() }; //Otherwise output the current character
var tails = input.Length > 1 //Is there any more string?
? Combinations(input.Substring(1)) //Yes, recursively compute
: new[] { "" }; //Otherwise, output empty string
//Now, join it up and return
return
from h in head
from t in tails
select h + t;
}
You don't need recursion here, you can enumerate your patterns and treat them as binary numbers. For example, if you have three zeros in your string, you get:
0 000 ....0..0....0...
1 001 ....0..0....o...
2 010 ....0..o....0...
3 011 ....0..o....o...
4 100 ....o..0....0...
5 101 ....o..0....o...
6 110 ....o..o....0...
7 111 ....o..o....o...
You can implement that with bitwise operators or by treating the chars that you want to replace like an odometer.
Below is an implementation in C. I'm not familiar with C# and from the other answers I see that C# already has suitable standard classes to implement what you want. (Although I'm surprised that so many peolpe propose recursion here.)
So this is more an explanation or illustration of my comment to the question than an implementation advice for your problem.
int binrep(char str[])
{
int zero[40]; // indices of zeros
int nzero = 0; // number of zeros in string
int ncombo = 1; // number of result strings
int i, j;
for (i = 0; str[i]; i++) {
if (str[i] == '0') {
zero[nzero++] = i;
ncombo <<= 1;
}
}
for (i = 0; i < ncombo; i++) {
for (j = 0; j < nzero; j++) {
str[zero[j]] = ((i >> j) & 1) ? 'o' : '0';
}
printf("%s\n", str); // should yield here
}
return ncombo;
}
Here's a solution using recursion, and your buffer array:
private static void Main(string[] args)
{
var a = Combinations("100");
var b = Combinations("10000");
}
public static IEnumerable<string> Combinations(string input)
{
var combinations = new List<string>();
combinations.Add(input);
for (int i = 0; i < input.Length; i++)
{
char[] buffer= input.ToArray();
if (buffer[i] == '0')
{
buffer[i] = 'o';
combinations.Add(new string(buffer));
combinations = combinations.Concat(Combinations(new string(buffer))).ToList();
}
}
return combinations.Distinct();
}
The method adds the raw input as the first result. After that, we replace in a loop the 0s we see as a o and call our method back with that new input, which will cover the case of multiple 0s.
Finally, we end up with a couple duplicates, so we use Distinct.
I know that the earlier answers are better. But I don't want my code to go to waste. :)
My approach for this combinatorics problem would be to take advantage of how binary numbers work. My algorithm would be as follows:
List<string> ZeroCombiner(string str)
{
// Get number of zeros.
var n = str.Count(c => c == '0');
var limit = (int)Math.Pow(2, n);
// Create strings of '0' and 'o' based on binary numbers from 0 to 2^n.
var binaryStrings = new List<string>();
for (int i = 0; i < limit; ++i )
{
binaryStrings.Add(Binary(i, n + 1));
}
// Replace each zero with respect to each binary string.
var result = new List<string>();
foreach (var binaryString in binaryStrings)
{
var zeroCounter = 0;
var combinedString = string.Empty;
for (int i = 0; i < str.Length; ++i )
{
if (str[i] == '0')
{
combinedString += binaryString[zeroCounter];
++zeroCounter;
}
else
combinedString += str[i];
}
result.Add(combinedString);
}
return result;
}
string Binary(int i, int n)
{
string result = string.Empty;
while (n != 0)
{
result = result + (i % 2 == 0 ? '0' : 'o');
i = i / 2;
--n;
}
return result;
}

Masking all characters of a string except for the last n characters

I want to know how can I replace a character of a string with condition of "except last number characters"?
Example:
string = "4111111111111111";
And I want to make it that
new_string = "XXXXXXXXXXXXX1111"
In this example I replace the character to "X" except the last 4 characters.
How can I possibly achieve this?
Would that suit you?
var input = "4111111111111111";
var length = input.Length;
var result = new String('X', length - 4) + input.Substring(length - 4);
Console.WriteLine(result);
// Ouput: XXXXXXXXXXXX1111
How about something like...
new_string = new String('X', YourString.Length - 4)
+ YourString.Substring(YourString.Length - 4);
create a new string based on the length of the current string -4 and just have it all "X"s. Then add on the last 4 characters of the original string
Here's a way to think through it. Call the last number characters to leave n:
How many characters will be replaced by X? The length of the string minus n.
How can we replace characters with other characters? You can't directly modify a string, but you can build a new one.
How to get the last n characters from the original string? There's a couple ways to do this, but the simplest is probably Substring, which allows us to grab part of a string by specifying the starting point and optionally the ending point.
So it would look something like this (where n is the number of characters to leave from the original, and str is the original string - string can't be the name of your variable because it's a reserved keyword):
// 2. Start with a blank string
var new_string = "";
// 1. Replace first Length - n characters with X
for (var i = 0; i < str.Length - n; i++)
new_string += "X";
// 3. Add in the last n characters from original string.
new_string += str.Substring(str.Length - n);
This might be a little Overkill for your ask. But here is a quick extension method that does this.
it defaults to using x as the masking Char but can be changed with an optional char
public static class Masking
{
public static string MaskAllButLast(this string input, int charsToDisplay, char maskingChar = 'x')
{
int charsToMask = input.Length - charsToDisplay;
return charsToMask > 0 ? $"{new string(maskingChar, charsToMask)}{input.Substring(charsToMask)}" : input;
}
}
Here a unit tests to prove it works
using Xunit;
namespace Tests
{
public class MaskingTest
{
[Theory]
[InlineData("ThisIsATest", 4, 'x', "xxxxxxxTest")]
[InlineData("Test", 4, null, "Test")]
[InlineData("ThisIsATest", 4, '*', "*******Test")]
[InlineData("Test", 16, 'x', "Test")]
[InlineData("Test", 0, 'y', "yyyy")]
public void Testing_Masking(string input, int charToDisplay, char maskingChar, string expected)
{
//Act
string actual = input.MaskAllButLast(charToDisplay, maskingChar);
//Assert
Assert.Equal(expected, actual);
}
}
}
StringBuilder sb = new StringBuilder();
Char[] stringChar = string.toCharArray();
for(int x = 0; x < stringChar.length-4; x++){
sb.append(stringChar[x]);
}
sb.append(string.substring(string.length()-4));
string = sb.toString();
I guess you could use Select with index
string input = "4111111111111111";
string new_string = new string(input.Select((c, i) => i < input.Length - 4 ? 'X' : c).ToArray());
Some of the other concise answers here did not account for strings less than n characters. Here's my take:
var length = input.Length;
input = length > 4 ? new String('*', length - 4) + input.Substring(length - 4) : input;
lui,
Please Try this one...
string dispString = DisplayString("4111111111111111", 4);
Create One function with pass original string and no of digit.
public string DisplayString(string strOriginal,int lastDigit)
{
string strResult = new String('X', strOriginal.Length - lastDigit) + strOriginal.Substring(strOriginal.Length - lastDigit);
return strResult;
}
May be help you....
Try this:
String maskedString = "...."+ (testString.substring(testString.length() - 4, testString.length()));
Late to the party but I also wanted to mask all but the last 'x' characters, but only mask numbers or letters so that any - ( ), other formatting, etc would still be shown. Here's my quick extension method that does this - hopefully it helps someone. I started with the example from Luke Hammer, then changed the guts to fit my needs.
public static string MaskOnlyChars(this string input, int charsToDisplay, char maskingChar = 'x')
{
StringBuilder sbOutput = new StringBuilder();
int intMaskCount = input.Length - charsToDisplay;
if (intMaskCount > 0) //only mask if string is longer than requested unmasked chars
{
for (var intloop = 0; intloop < input.Length; intloop++)
{
char charCurr = Char.Parse(input.Substring(intloop, 1));
byte[] charByte = Encoding.ASCII.GetBytes(charCurr.ToString());
int intCurrAscii = charByte[0];
if (intloop <= (intMaskCount - 1))
{
switch (intCurrAscii)
{
case int n when (n >= 48 && n <= 57):
//0-9
sbOutput.Append(maskingChar);
break;
case int n when (n >= 65 && n <= 90):
//A-Z
sbOutput.Append(maskingChar);
break;
case int n when (n >= 97 && n <= 122):
//a-z
sbOutput.Append(maskingChar);
break;
default:
//Leave other characters unmasked
sbOutput.Append(charCurr);
break;
}
}
else
{
//Characters at end to remain unmasked
sbOutput.Append(charCurr);
}
}
}
else
{
//if not enough characters to mask, show unaltered input
return input;
}
return sbOutput.ToString();
}

Categories