Can I identify string [] inner value? - c#

I have a task and I have to check how many odd numbers there are. For example:
cw(string[54]); //37 42 44 61 62
From this I need to get how many odd numbers are in this string. The only way I figured out was to cut the string into 5 ints so int 1 is 37, 2 is 42 and so on. But that is a really long and slow process even with methods.
Any help, or shall I stick with the "cutting" which looks something like this:
for (int y = 0; y < all_number.Length; y++)
{
for (int x = 0; x < 5; x++)
{
cutter = all_number[y];
placeholder = cutter.IndexOf(" ");
final[x] = Convert.ToInt32(cutter.Remove(placeholder));
}
}
This one is for the first numbers, so at 37 42 44 61 62 final would be 37.

I'd start by using an outer foreach loop rather than referring array values by index, unless the index is important (which it doesn't look like it is here).
I'd then use string.Split to split each string by spaces, and then LINQ to sum the odd numbers.
For example:
foreach (string line in lines)
{
var oddSum = line.Split(' ')
.Select(int.Parse) // Parse each chunk
.Where(number => (number & 1) == 1) // Filter out even values
.Sum(); // Sum all the odd values
// Do whatever you want with the sum of the odd values for this line
}
If you actually only want to count the odd numbers, you can use the overload of Count that accepts a predicate:
foreach (string line in lines)
{
var oddCount = line.Split(' ')
.Select(int.Parse) // Parse each chunk
.Count(number => (number & 1) == 1) // Count the odd values
// Do whatever you want with the count of the odd values for this line
}
Note that this will throw an exception (in int.Parse) at the first non-integer value encountered. That may well be fine, but you can use int.TryParse to avoid the exception. That's harder to use with LINQ though; please specify how you want them handled if you need this functionality.

First of all, use the built in tools you have available.
To split a string by a predefined character, use Split:
var numbers = allNumbersString.Split(' ');
Now you have an array of strings, each holding a string representation of what we hope is a number.
Now we need to extract numbers out of each string. The safest way to do this is using int.TryParse:
foreach (var n in numbers)
{
if (int.TryParse(out var number)
{
//ok we got a number
}
else
{
//we don't. Do whatever is appropriate:
//ignore invalid number, log parse failure, throw, etc.
}
}
And now, simply return those that are odd: number % 2 != 0;
Putting it all together:
public static IEnumerable<int> ExtractOddNumbers(
string s
char separator)
{
if (s == null)
throw new ArgumentNullException(name(s));
foreach (var n in s.Split(separator))
{
if (int.TryParse(out var number)
{
if (number % 2 != 0)
yield return number;
}
}
}
So, if you want to know how many odd numbers there are in a given string, you would do:
var countOfOddNumbers = ExtractOddNumbers(s, ' ').Count();
The good thing about this approach is that now, its easily extensible. A small modification to our current method makes it a whole lot more powerful:
public static IEnumerable<int> ExtractNumbers(
string s
char separator
Func<int, bool> predicate)
{
if (s == null)
throw new ArgumentNullException(name(s));
foreach (var n in s.Split(separator))
{
if (int.TryParse(out var number)
{
if (predicate(number))
yield return number;
}
}
}
See what we've done? We've made the filtering criteria one more argument of the method call; now you can extract numbers based on any condition. Odd numbers? ExtractNumbers(s, ' ', n => n % 2 != 0). Multiples of 7? ExtractNumbers(s, ' ', n => n % 7 == 0). Greater than 100? ExtractNumbers(s, ' ', n => n > 100), etc.

As mentioned by others, the Split method is what you're after.
if you want the count of odd numbers then you can accomplish the task like so:
var oddCount = lines.SelectMany(line => line.Split(' ')) // flatten
.Select(int.Parse) // parse the strings
.Count(n => n % 2 != 0); // count the odd numbers
or if you want the summation you can do:
var oddSum = lines.SelectMany(line => line.Split(' '))// flatten
.Select(int.Parse) // parse the strings
.Where(n => n % 2 != 0)// retain the odd numbers
.Sum();// sum them
This assumes there will be no invalid characters in the string, otherwise, you'll need to perform checks with the Where clause prior to proceeding.

An alternative would be to loop over the string's characters and, if the current character is a space or the end of the string and the previous character is '1', '3', '5', '7' or '9' (odd numbers end with an odd figure), increase the count.
This allows the string to contain numbers that are much bigger than ints, does not allocate new memory (as with String.Split) and doesn't require the parsing of ints. It does assume a valid string with valid numbers:
var count = 0;
for(var i = 1; i < cw.Length; i++)
{
int numberIndex = -1;
if(i == cw.Length - 1) numberIndex = i;
if(cw[i] == ' ') numberIndex = i - 1;
if(numberIndex != -1)
{
if(cw[numberIndex] == '1' || cw[numberIndex] == '3' ||
cw[numberIndex] == '5' || cw[numberIndex] == '7' ||
cw[numberIndex] == '9')
{
count++;
}
}
}

Related

How do you do a string split with 2 chars counts in C#?

I know how to do a string split if there's a letter, number, that I want to replace.
But how could I do a string.Split() by 2 char counts without replacing any existing letters, number, etc...?
Example:
string MAC = "00122345"
I want that string to output: 00:12:23:45
You could create a LINQ extension method to give you an IEnumerable<string> of parts:
public static class Extensions
{
public static IEnumerable<string> SplitNthParts(this string source, int partSize)
{
if (string.IsNullOrEmpty(source))
{
throw new ArgumentException("String cannot be null or empty.", nameof(source));
}
if (partSize < 1)
{
throw new ArgumentException("Part size has to be greater than zero.", nameof(partSize));
}
return Enumerable
.Range(0, (source.Length + partSize - 1) / partSize)
.Select(pos => source
.Substring(pos * partSize,
Math.Min(partSize, source.Length - pos * partSize)));
}
}
Usage:
var strings = new string[] {
"00122345",
"001223453"
};
foreach (var str in strings)
{
Console.WriteLine(string.Join(":", str.SplitNthParts(2)));
}
// 00:12:23:45
// 00:12:23:45:3
Explanation:
Use Enumerable.Range to get number of positions to slice string. In this case its the length of the string + chunk size - 1, since we need to get a big enough range to also fit leftover chunk sizes.
Enumerable.Select each position of slicing and get the startIndex using String.Substring using the position multiplied by 2 to move down the string every 2 characters. You will have to use Math.Min to calculate the smallest size leftover size if the string doesn't have enough characters to fit another chunk. You can calculate this by the length of the string - current position * chunk size.
String.Join the final result with ":".
You could also replace the LINQ query with yield here to increase performance for larger strings since all the substrings won't be stored in memory at once:
for (var pos = 0; pos < source.Length; pos += partSize)
{
yield return source.Substring(pos, Math.Min(partSize, source.Length - pos));
}
You can use something like this:
string newStr= System.Text.RegularExpressions.Regex.Replace(MAC, ".{2}", "$0:");
To trim the last colon, you can use something like this.
newStr.TrimEnd(':');
Microsoft Document
Try this way.
string MAC = "00122345";
MAC = System.Text.RegularExpressions.Regex.Replace(MAC,".{2}", "$0:");
MAC = MAC.Substring(0,MAC.Length-1);
Console.WriteLine(MAC);
A quite fast solution, 8-10x faster than the current accepted answer (regex solution) and 3-4x faster than the LINQ solution
public static string Format(this string s, string separator, int length)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.Length; i += length)
{
sb.Append(s.Substring(i, Math.Min(s.Length - i, length)));
if (i < s.Length - length)
{
sb.Append(separator);
}
}
return sb.ToString();
}
Usage:
string result = "12345678".Format(":", 2);
Here is a one (1) line alternative using LINQ Enumerable.Aggregate.
string result = MAC.Aggregate("", (acc, c) => acc.Length % 3 == 0 ? acc += c : acc += c + ":").TrimEnd(':');
An easy to understand and simple solution.
This is a simple fast modified answer in which you can easily change the split char.
This answer also checks if the number is even or odd , to make the suitable string.Split().
input : 00122345
output : 00:12:23:45
input : 0012234
output : 00:12:23:4
//The List that keeps the pairs
List<string> MACList = new List<string>();
//Split the even number into pairs
for (int i = 1; i <= MAC.Length; i++)
{
if (i % 2 == 0)
{
MACList.Add(MAC.Substring(i - 2, 2));
}
}
//Make the preferable output
string output = "";
for (int j = 0; j < MACList.Count; j++)
{
output = output + MACList[j] + ":";
}
//Checks if the input string is even number or odd number
if (MAC.Length % 2 == 0)
{
output = output.Trim(output.Last());
}
else
{
output += MAC.Last();
}
//input : 00122345
//output : 00:12:23:45
//input : 0012234
//output : 00:12:23:4

Finding the largest possible number in permutations of digits less than a limit

I want to permute the digits of an int in a way that the result is the biggest possible permutation. This is easily done like this:
//how to deal with really large ints e.g.int32.MaxValue goes here
// in this case the algorithm would need to round down to the largest possible value
// but still needs to be less than int32.MaxValue
//This code will just handle normal values <int32.MaxValue
public static int Max(int number)
{
var numberAsCharArray = number.ToString().OrderByDescending(c => c).ToArray();
var largestNumberAsString = new string(numberAsCharArray);
return Int32.Parse(largestNumberAsString);
}
However, when the input has the same number of digits as Int32.MaxValue and contains at least one high digit, this digit will go to the first position making the result > Int32.MaxValue and leading to an exception when converting to int.
How could I limit the result to be <= Int32.MaxValue but within this limit still be the greatest possible permutation?
N.B. Negative numbers, e.g. -1234567890 are allowed as well as positive ones; in case of negative input, - sign should be dropped: -1234567890 should produce 2147398650 output
For small numbers (less or equal to 1000000000) you can do the business as usual; for numbers greater than one billion
you can try the follow approach:
Try follow int.MaxValue pattern (2147483647) as long as it's possible
When it's not possible, put the maximum number you can do and continue doing the business as usual for the remaining digits.
For instance, given 1234567890
1234567890 <- initial value
2147483647 <- int.MaxValue pattern
2147398650 <- solution
^
|
Here we can't put another 4, we put maximum available - 3
Remaining digits [56890] we order by descending - "98650" - business as usual
Implementation
private static int Biggest(int value) {
// Special MinValue case;
// we can't do Math.Abs() because of integer overflow
if (value == int.MinValue)
return 2147483486;
string st = value.ToString().Trim('-');
if (value <= 1000000000 && value >= -1000000000)
return int.Parse(string.Concat(st.OrderByDescending(c => c)));
string max = int.MaxValue.ToString();
List<int> digits = st.Select(c => c - '0').ToList();
StringBuilder sb = new StringBuilder(9);
bool exact = true;
while (digits.Any()) {
for (int i = 0; i < max.Length; ++i) {
int digitToFind = max[i] - '0';
int digitActual;
digitActual = digits
.Where(d => !exact || d <= digitToFind)
.OrderByDescending(d => d)
.First();
if (exact)
exact = digitActual == digitToFind;
sb.Append(digitActual);
digits.Remove(digitActual);
}
}
return int.Parse(sb.ToString());
}
Test:
// 2147398650 (for reference: int.MaxValue == 2147483647)
Console.WriteLine(Biggest(1234567890));
I suggest this:
int number = 587;
int maximum = Int32.MaxValue;
var result = "";
if(number == Int32.MinValue)
{
result = "2147483486";
}
else
{
// create list of available digits removing - sign from the string
var inputDigits = number.ToString().Replace("-", String.Empty).Select(c => Int32.Parse(new string(c, 1))).ToList();
var limitDigits = maximum.ToString().Select(c => Int32.Parse(new string(c, 1))).ToList();
var orderedDigits = inputDigits.OrderByDescending(c => c).ToList();
int position = 0;
// we only have to compare to the maximum if we have at least the same amount of digits in the input.
bool compareValues = limitDigits.Count <= inputDigits.Count;
// while we have not used all of the digits
while (orderedDigits.Count > 0)
{
// loop over the remaining digits from high to low values
for (int i = 0; i < orderedDigits.Count; i++)
{
// if it is above the digit in the maximum at the corresponding place we may only use it if input is shorter than maximum or if we have already used a lower value in a previous digit.
if (orderedDigits[i] > limitDigits[position])
{
if (compareValues)
{
continue;
}
}
else if (orderedDigits[i] < limitDigits[position])
{
// remember that we have already used a lower value
compareValues = false;
}
result += (orderedDigits[i].ToString());
orderedDigits.RemoveAt(i);
break;
}
position++;
}
}
var intResult = Int32.Parse(result);
EDIT:
inserted .Replace("-", String.Empty) when defining inputDigits to support negative numbers

c# read file content code optimization

I have a large string which is converted from a text file (eg 1 MB text 0file) and I want to process the string. It takes near 10 minutes to process the string.
Basically string is read character by character and increment counter for each character by one, some characters such as space, comma, colon and semi-colon are counted as space and rest characters are just ignored and thus space's counter is incremented.
Code:
string fileContent = "....." // a large string
int min = 0;
int max = fileContent.Length;
Dictionary<char, int> occurrence // example c=>0, m=>4, r=>8 etc....
// Note: occurrence has only a-z alphabets, and a space. comma, colon, semi-colon are coutned as space and rest characters ignored.
for (int i = min; i <= max; i++) // run loop to end
{
try // increment counter for alphabets and space
{
occurrence[fileContent[i]] += 1;
}
catch (Exception e) // comma, colon and semi-colon are spaces
{
if (fileContent[i] == ' ' || fileContent[i] == ',' || fileContent[i] == ':' || fileContent[i] == ';')
{
occurrence[' '] += 1;
//new_file_content += ' ';
}
else continue;
}
totalFrequency++; // increment total frequency
}
Try this:
string input = "test string here";
Dictionary<char, int> charDict = new Dictionary<char, int>();
foreach(char c in input.ToLower()) {
if(c < 97 || c > 122) {
if(c == ' ' || c == ',' || c == ':' || c == ';') {
charDict[' '] = (charDict.ContainsKey(' ')) ? charDict[' ']++ : 0;
}
} else {
charDict[c] = (charDict.ContainsKey(c)) ? charDict[c]++ : 0;
}
}
Given your loop is iterating through a large number you want to minimize the checks inside the loop and remove the catch which is pointed out in the comments. There should never be a reason to control flow logic with a try catch block. I would assume you initialize the dictionary first to set the occurrence cases to 0 otherwise you have to add to the dictionary if the character is not there. In the loop you can test the character with something like char.IsLetter() or other checks as D. Stewart is suggesting. I would not do a toLower on the large string if you are going to iterate every character anyway (this would do the iteration twice). You can do that conversion in the loop if needed.
Try something like the below code. You could also initialize all 256 possible characters in the dictionary and completely remove the if statement and then remove items you don't care about and add the 4 space items to the space character dictionary after the counting is complete.
foreach (char c in fileContent)
{
if (char.IsLetter(c))
{
occurrence[c] += 1;
}
else
{
if (c == ' ' || c == ',' || c == ':' || c == ';')
{
occurrence[' '] += 1;
}
}
}
}
You could initialize the entire dictionary in advance like this also:
for (int i = 0; i < 256; i++)
{
occurrence.Add((char)i, 0);
}
There are several issues with that code snippet (i <= max, accessing dictionary entry w/o being initialized etc.), but of course the performance bottleneck is relying on exceptions, since throwing / catching exceptions is extremely slow (especially when done in a inner loop).
I would start with putting the counts into a separate array.
Then I would either prepare a char to count index map and use it inside the loop w/o any ifs:
var indexMap = new Dictionary<char, int>();
int charCount = 0;
// Map the valid characters to be counted
for (var ch = 'a'; ch <= 'z'; ch++)
indexMap.Add(ch, charCount++);
// Map the "space" characters to be counted
foreach (var ch in new[] { ' ', ',', ':', ';' })
indexMap.Add(ch, charCount);
charCount++;
// Allocate count array
var occurences = new int[charCount];
// Process the string
foreach (var ch in fileContent)
{
int index;
if (indexMap.TryGetValue(ch, out index))
occurences[index]++;
}
// Not sure about this, but including it for consistency
totalFrequency = occurences.Sum();
or not use dictionary at all:
// Allocate array for char counts
var occurences = new int['z' - 'a' + 1];
// Separate count for "space" chars
int spaceOccurences = 0;
// Process the string
foreach (var ch in fileContent)
{
if ('a' <= ch && ch <= 'z')
occurences[ch - 'a']++;
else if (ch == ' ' || ch == ',' || ch == ':' || ch == ';')
spaceOccurences++;
}
// Not sure about this, but including it for consistency
totalFrequency = spaceOccurences + occurences.Sum();
The former is more flexible (you can add more mappings), the later - a bit faster. But both are fast enough (complete in milliseconds for 1M size string).
Ok, it´s a little late, but it should be the fastest solution:
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication99
{
class Program
{
static void Main(string[] args)
{
string fileContent = "....."; // a large string
// --- high perf section to count all chars ---
var charCounter = new int[char.MaxValue + 1];
for (int i = 0; i < fileContent.Length; i++)
{
charCounter[fileContent[i]]++;
}
// --- combine results with linq (all actions consume less than 1 ms) ---
var allResults = charCounter.Select((count, index) => new { count, charValue = (char)index }).Where(c => c.count > 0).ToArray();
var spaceChars = new HashSet<char>(" ,:;");
int countSpaces = allResults.Where(c => spaceChars.Contains(c.charValue)).Sum(c => c.count);
var usefulChars = new HashSet<char>("abcdefghijklmnopqrstuvwxyz");
int countLetters = allResults.Where(c => usefulChars.Contains(c.charValue)).Sum(c => c.count);
}
}
}
for very large text-files, it´s better to use the StreamReader...

Split string after variable amount of characters

I want to split my string called: invoerstring after a variable amount of characters (n is the number of characters when the string needs to be split)..
If the string length is shorter then the variable n, spaces need to be added until the string length = n. The result needs to be shown in a textfield called uitvoer.
This is what so far:
string invoerstring = invoer.Text;
if (invoerstring.Length < n)
{
invoerstring += "";
char[] myStrChars = invoerstring.ToCharArray();
}
if (invoerstring.Length == n)
{
string [] blok = invoerstring.Split();
foreach (string word in blok)
{
uitvoer.Text = word;
}
}
EDIT:
The solutions given above aren't completely doing the job for me, maybe it helps when I post the exercise:
|| crypt n m d text || text is padded with spaces until its length is
a multiple of n || the characters in text are circulary shifted in the
alphabet by the displacement d || example: if d = 1 then 'a' -> 'b' ,
'b' -> 'c' .... etc... 'z' -> 'a' || text is divided in blocks of
length n characters || inside every block of n the characters are
circulary shifted m times to the left || the shifted groups are
concatenated
I already solved the m and d only have to solve the n.
The solutions given above aren't completely doing the job for me, maybe it helps when I post the exercise:
|| crypt n m d text
|| text is padded with spaces until its length is a multiple of n
|| the characters in text are circulary shifted in the alphabet by the displacement d
|| example: if d = 1 then 'a' -> 'b' , 'b' -> 'c' .... etc... 'z' -> 'a'
|| text is divided in blocks of length n characters
|| inside every block of n the characters are circulary shifted m times to the left
|| the shifted groups are concatenated
I already solved the m and d only have to solve the n.
Here's the code you want, no need to go through a character array:
public static string EnsureExactLength(this string s, int length)
{
if (s == null)
throw new ArgumentNullException("null");
return s.PadRight(length).Substring(0, length);
}
You call it like this:
string s = "Test string".EnsureExactLength(4); // will return "Test"
string s = "Te".EnsureExactLength(4); // will return "Te "
You can find an example LINQPad program here.
Okay, I'm honestly not sure what the code you have above is doing because I see calls like Split() without any parameters, and stuff. But to meet the requirements, this one line should do:
string invoerstring = invoer.Text.PadRight(n, ' ').Substring(0, n);
the PadRight will make sure it's as long as n and the Substring will then return the portion of the string up to n.
If you then wanted that string in an array, because I see you have one at the end, you could do this:
invoerstring.ToArray();
Here is a LinqPad script:
void Main()
{
const string TEXT = "12345ABCDE12345ABCDE1234";
const int LENGTH = 5;
const char PAD = '#';
Enumerable.Range(0, TEXT.Length / LENGTH)
.Union(TEXT.Length < LENGTH ? new int[] { 1 } : new int[] {})
.Select((index, item) => TEXT.Length < LENGTH
? TEXT.PadRight(LENGTH, PAD)
: TEXT.Substring(index * LENGTH, LENGTH))
.Concat(TEXT.Length % LENGTH != 0
? new string[] { TEXT.Substring(TEXT.Length - (TEXT.Length % LENGTH)).PadRight(LENGTH, PAD) }
: new string[] { })
.Dump();
}

What is the most efficient way to detect if a string contains a number of consecutive duplicate characters in C#?

For example, a user entered "I love this post!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
the consecutive duplicate exclamation mark "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" should be detected.
The following regular expression would detect repeating chars. You could up the number or limit this to specific characters to make it more robust.
int threshold = 3;
string stringToMatch = "thisstringrepeatsss";
string pattern = "(\\d)\\" + threshold + " + ";
Regex r = new Regex(pattern);
Match m = r.Match(stringToMatch);
while(m.Success)
{
Console.WriteLine("character passes threshold " + m.ToString());
m = m.NextMatch();
}
Here's and example of a function that searches for a sequence of consecutive chars of a specified length and also ignores white space characters:
public static bool HasConsecutiveChars(string source, int sequenceLength)
{
if (string.IsNullOrEmpty(source))
return false;
if (source.Length == 1)
return false;
int charCount = 1;
for (int i = 0; i < source.Length - 1; i++)
{
char c = source[i];
if (Char.IsWhiteSpace(c))
continue;
if (c == source[i+1])
{
charCount++;
if (charCount >= sequenceLength)
return true;
}
else
charCount = 1;
}
return false;
}
Edit fixed range bug :/
Can be done in O(n) easily: for each character, if the previous character is the same as the current, increment a temporary count. If it's different, reset your temporary count. At each step, update your global if needed.
For abbccc you get:
a => temp = 1, global = 1
b => temp = 1, global = 1
b => temp = 2, global = 2
c => temp = 1, global = 2
c => temp = 2, global = 2
c => temp = 3, global = 3
=> c appears three times. Extend it to get the position, then you should be able to print the "ccc" substring.
You can extend this to give you the starting position fairly easily, I'll leave that to you.
Here is a quick solution I crafted with some extra duplicates thrown in for good measure. As others pointed out in the comments, some duplicates are going to be completely legitimate, so you may want to narrow your criteria to punctuation instead of mere characters.
string input = "I loove this post!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!aa";
int index = -1;
int count =1;
List<string> dupes = new List<string>();
for (int i = 0; i < input.Length-1; i++)
{
if (input[i] == input[i + 1])
{
if (index == -1)
index = i;
count++;
}
else if (index > -1)
{
dupes.Add(input.Substring(index, count));
index = -1;
count = 1;
}
}
if (index > -1)
{
dupes.Add(input.Substring(index, count));
}
The better way i my opinion is create a array, each element in array is responsible for one character pair on string next to each other, eg first aa, bb, cc, dd. This array construct with 0 on each element.
Solve of this problem is a for on this string and update array values.
You can next analyze this array for what you want.
Example: For string: bbaaaccccdab, your result array would be { 2, 1, 3 }, because 'aa' can find 2 times, 'bb' can find one time (at start of string), 'cc' can find three times.
Why 'cc' three times? Because 'cc'cc & c'cc'c & cc'cc'.
Use LINQ! (For everything, not just this)
string test = "aabb";
return test.Where((item, index) => index > 0 && item.Equals(test.ElementAt(index)));
// returns "abb", where each of these items has the previous letter before it
OR
string test = "aabb";
return test.Where((item, index) => index > 0 && item.Equals(test.ElementAt(index))).Any();
// returns true

Categories

Resources