Formatting a C# string with identical spacing in between values - c#

I have 3 strings. The first set of strings are:
"1.0536"
"2.1"
"2"
The second is something like:
"Round"
"Square"
"Hex"
And the last are:
"6061-T6"
"T351"
"ASF.3.4.5"
I need to combine the three strings together with identical spacing in between each string. I can't use \t for tabbing as after I combine the strings, I send them to an Access Database.
When I combine the strings they look like:
"1.0536 Round 6061-T6"
"2.1 Square T351"
"2 Hex ASF.3.4.5"
I would really like them to look like this with the same exact amount of spacing in between each string:
"1.0536 Round 6061-T6"
"2.1 Square T351"
"2 Hex ASF.3.4.5"
How can I do this with C#?

You can use advanced features of string.Format:
string.Format("{0,-10}{1,-10}{2}", ...)
You can do the same thing by writing str.PadRight(10)

If you know the maximum lengths of each column then do the following:
String result = String.Format("{0} {1} {2}", strCol1.PadRight(10), strCol2.PadRight(9), strCol3.PadRight(9));

To make life easier, utility methods:
Usage
var data = new[] {
new[] { "ID", "NAME", "DESCRIPTION" },
new[] { "1", "Frank Foo", "lorem ipsum sic dolor" },
new[] { "2", "Brandon Bar", "amet forthrightly" },
new[] { "3", "B. Baz", "Yeehah!" }
};
var tabbedData = EvenColumns(20, data);
var tabbedData2 = string.Join("\n", EvenColumns(20, false, data)); // alternate line separator, alignment
Results
ID NAME DESCRIPTION
1 Frank Foo lorem ipsum sic dolor
2 Brandon Bar amet forthrightly
3 B. Baz Yeehah!
ID NAME DESCRIPTION
1 Frank Foolorem ipsum sic dolor
2 Brandon Bar amet forthrightly
3 B. Baz Yeehah!
Code
public string EvenColumns(int desiredWidth, IEnumerable<IEnumerable<string>> lists) {
return string.Join(Environment.NewLine, EvenColumns(desiredWidth, true, lists));
}
public IEnumerable<string> EvenColumns(int desiredWidth, bool rightOrLeft, IEnumerable<IEnumerable<string>> lists) {
return lists.Select(o => EvenColumns(desiredWidth, rightOrLeft, o.ToArray()));
}
public string EvenColumns(int desiredWidth, bool rightOrLeftAlignment, string[] list, bool fitToItems = false) {
// right alignment needs "-X" 'width' vs left alignment which is just "X" in the `string.Format` format string
int columnWidth = (rightOrLeftAlignment ? -1 : 1) *
// fit to actual items? this could screw up "evenness" if
// one column is longer than the others
// and you use this with multiple rows
(fitToItems
? Math.Max(desiredWidth, list.Select(o => o.Length).Max())
: desiredWidth
);
// make columns for all but the "last" (or first) one
string format = string.Concat(Enumerable.Range(rightOrLeftAlignment ? 0 : 1, list.Length-1).Select( i => string.Format("{{{0},{1}}}", i, columnWidth) ));
// then add the "last" one without Alignment
if(rightOrLeftAlignment) {
format += "{" + (list.Length-1) + "}";
}
else {
format = "{0}" + format;
}
return string.Format(format, list);
}
Specific to the Question
// for fun, assume multidimensional declaration rather than jagged
var data = new[,] {
{ "1.0536", "2.1", "2" },
{ "Round", "Square", "Hex" },
{ "6061-T6", "T351", "ASF.3.4.5" },
};
var tabbedData = EvenColumns(20, Transpose(ToJaggedArray(data)));
with Transpose:
public T[][] Transpose<T>(T[][] original) {
// flip dimensions
var h = original.Length;
var w = original[0].Length;
var result = new T[h][];
for (var r = 0; r < h; r++) {
result[r] = new T[w];
for (var c = 0; c < w; c++)
{
result[r][c] = original[c][r];
}
}
return result;
}
And multidimensional arrays (source):
public T[][] ToJaggedArray<T>(T[,] multiArray) {
// via https://stackoverflow.com/questions/3010219/jagged-arrays-multidimensional-arrays-conversion-in-asp-net
var h = multiArray.GetLength(0);
var w = multiArray.GetLength(1);
var result = new T[h][];
for (var r = 0; r < h; r++) {
result[r] = new T[w];
for (var c = 0; c < w; c++) {
result[r][c] = multiArray[r, c];
}
}
return result;
}

I know this has long since been answered, but there is a new way as of C# 6.0
string[] one = new string[] { "1.0536", "2.1", "2" };
string[] two = new string[] { "Round", "Square", "Hex" };
string[] three = new string[] { "1.0536 Round 6061-T6", "2.1 Square T351", "2 Hex ASF.3.4.5" };
for (int i = 0; i < 3; i++) Console.WriteLine($"{one[i],-10}{two[i],-10}{three[i],-10}");
The $"{one[i],-10}{two[i],-10}{three[i],-10}" is the new replacement for string.format . I have found it very useful in many of my projects. Here is a link to more information about string interpolation in c# 6.0:
https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/string-interpolation

Use String.Format("{0,10}", myString)
Where 10 is the number of characters you want

To do it more dynamically you could do something like this: (hardcoding ahead!)
int padding = 3;
int maxCol0width = "Hello World!".Length;
int maxCol1width = "90.345".Length;
int maxCol2width = "value".Length;
string fmt0 = "{0,-" + (maxCol0width + padding) + "}";
string fmt1 = "{1,-" + (maxCol1width + padding) + "}";
string fmt2 = "{2,-" + (maxCol2width + padding) + "}";
string fmt = fmt0 + fmt1 + fmt2;
Console.WriteLine(fmt, "Hello World!", 90.345, "value");
Console.WriteLine(fmt, "Hi!", 1.2, "X");
Console.WriteLine(fmt, "Another", 100, "ZZZ");
You will of course need to figure out your max word widths by looping through each column's values. Also the creation of the format string could be significantly cleaned up and shortened.

Also note that you will need to use a non-proportional font for display, otherwise your columns will still not line up properly. Where are you displaying this data? There may be better ways of getting tabular output.

Related

Get count of unique characters between first and last letter

I'm trying to get the unique characters count that are between the first and last letter of a word. For example: if I type Yellow the expected output is Y3w, if I type People the output should be P4e and if I type Money the output should be M3y. This is what I tried:
//var strArr = wordToConvert.Split(' ');
string[] strArr = new[] { "Money","Yellow", "People" };
List<string> newsentence = new List<string>();
foreach (string word in strArr)
{
if (word.Length > 2)
{
//ignore 2-letter words
string newword = null;
int distinctCount = 0;
int k = word.Length;
int samecharcount = 0;
int count = 0;
for (int i = 1; i < k - 2; i++)
{
if (word.ElementAt(i) != word.ElementAt(i + 1))
{
count++;
}
else
{
samecharcount++;
}
}
distinctCount = count + samecharcount;
char frst = word[0];
char last = word[word.Length - 1];
newword = String.Concat(frst, distinctCount.ToString(), last);
newsentence.Add(newword);
}
else
{
newsentence.Add(word);
}
}
var result = String.Join(" ", newsentence.ToArray());
Console.WriteLine("Output: " + result);
Console.WriteLine("----------------------------------------------------");
With this code I'm getting the expect output for Yellow, but seems that is not working with People and Money. What can I do to fix this issue or also I'm wondering is maybe there is a better way to do this for example using LINQ/Regex.
Here's an implementation that uses Linq:
string[] strArr = new[]{"Money", "Yellow", "People"};
List<string> newsentence = new List<string>();
foreach (string word in strArr)
{
if (word.Length > 2)
{
// we want the first letter, the last letter, and the distinct count of everything in between
var first = word.First();
var last = word.Last();
var others = word.Skip(1).Take(word.Length - 2);
// Case sensitive
var distinct = others.Distinct();
// Case insensitive
// var distinct = others.Select(c => char.ToLowerInvariant(c)).Distinct();
string newword = first + distinct.Count().ToString() + last;
newsentence.Add(newword);
}
else
{
newsentence.Add(word);
}
}
var result = String.Join(" ", newsentence.ToArray());
Console.WriteLine(result);
Output:
M3y Y3w P4e
Note that this doesn't take account of case, so the output for FiIsSh is 4.
Maybe not the most performant, but here is another example using linq:
var words = new[] { "Money","Yellow", "People" };
var transformedWords = words.Select(Transform);
var sentence = String.Join(' ', transformedWords);
public string Transform(string input)
{
if (input.Length < 3)
{
return input;
}
var count = input.Skip(1).SkipLast(1).Distinct().Count();
return $"{input[0]}{count}{input[^1]}";
}
You can implement it with the help of Linq. e.g. (C# 8+)
private static string EncodeWord(string value) => value.Length <= 2
? value
: $"{value[0]}{value.Substring(1, value.Length - 2).Distinct().Count()}{value[^1]}";
Demo:
string[] tests = new string[] {
"Money","Yellow", "People"
};
var report = string.Join(Environment.NewLine, tests
.Select(test => $"{test} :: {EncodeWord(test)}"));
Console.Write(report);
Outcome:
Money :: M3y
Yellow :: Y3w
People :: P4e
A lot of people have put up some good solutions. I have two solutions for you: one uses LINQ and the other does not.
LINQ, Probably not much different from others
if (str.Length < 3) return str;
var midStr = str.Substring(1, str.Length - 2);
var midCount = midStr.Distinct().Count();
return string.Concat(str[0], midCount, str[str.Length - 1]);
Non-LINQ
if (str.Length < 3) return str;
var uniqueLetters = new Dictionary<char, int>();
var midStr = str.Substring(1, str.Length - 2);
foreach (var c in midStr)
{
if (!uniqueLetters.ContainsKey(c))
{
uniqueLetters.Add(c, 0);
}
}
var midCount = uniqueLetters.Keys.Count();
return string.Concat(str[0], midCount, str[str.Length - 1]);
I tested this with the following 6 strings:
Yellow
Money
Purple
Me
You
Hiiiiiiiii
Output:
LINQ: Y3w, Non-LINQ: Y3w
LINQ: M3y, Non-LINQ: M3y
LINQ: P4e, Non-LINQ: P4e
LINQ: Me, Non-LINQ: Me
LINQ: Y1u, Non-LINQ: Y1u
LINQ: H1i, Non-LINQ: H1i
Fiddle
Performance-wise I'd guess they're pretty much the same, if not identical, but I haven't run any real perf test on the two approaches. I can't imagine they'd be much different, if at all. The only real difference is that the second route expands Distinct() into what it probably does under the covers anyway (I haven't looked at the source to see if that's true, but that's a pretty common way to get a count of . And the first route is certainly less code.
I Would use Linq for that purpose:
string[] words = new string[] { "Yellow" , "People", "Money", "Sh" }; // Sh for 2 letter words (or u can insert 0 and then remove the trinary operator)
foreach (string word in words)
{
int uniqeCharsInBetween = word.Substring(1, word.Length - 2).ToCharArray().Distinct().Count();
string result = word[0] + (uniqeCharsInBetween == 0 ? string.Empty : uniqeCharsInBetween.ToString()) + word[word.Length - 1];
Console.WriteLine(result);
}

Continuously reading serial-port buffer and parsing data to array in C#

Hello I'm trying to find best solution for continuously reading serial-port buffer and parsing data to array, continuously string form serial-port looks like this :
"...G 130 g\r\nG 130 g\r\nG 124 g\r\nG 124 g\r\nG 124
g\r\nG... "
The main idea is read for exaple last 3 data from serial-port broadcasting string, parse to double array [124.0; 124.0; 124.0] and comapre if equals.
Now I'm using this simple code but it is not enough fast :
_serialPortMainForm.DataReceived += new SerialDataReceivedEventHandler(Port_DataReceived);
.....
void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] data = new byte[_serialPortMainForm.BytesToRead];
_serialPortMainForm.Read(data, 0, data.Length);
string str = Encoding.ASCII.GetString(data);
RawSerialPortData += str;
if (RawSerialPortData.Length > Properties.Settings.Default.BufferSize) {
ProcessSerialPortData(RawSerialPortData);
RawSerialPortData = "";
}
}
And parsing data like this :
public void ProcessSerialPortData( string DataToProcess) {
int count = 0;
string PostFix = "";
DataToProcess = DataToProcess.Substring(DataToProcess.IndexOf('G') + 1);
DataToProcess = DataToProcess.Replace(" ", "");
DataToProcess = DataToProcess.Replace(Environment.NewLine, "|");
foreach (char c in DataToProcess) if (c == '|') count++;
ProcessedData = new double[count-1];
DataToProcess = DataToProcess.Replace("G", "");
if (DataToProcess.Contains("kg")) { PostFix = "kg"; DataToProcess = DataToProcess.Replace("kg", ""); }
if (DataToProcess.Contains("g")) { PostFix = "g"; DataToProcess = DataToProcess.Replace("g", ""); }
UnitsController(PostFix);
DataToProcess = DataToProcess.Replace(".", ",");
string[] SplitData = DataToProcess.Split('|');
int j = 0;
while (j < count - 1) {
Double.TryParse(SplitData[j], out ProcessedData[j]);
j++;
}
}
Kg are irelevant for this situation because grams are enough for this situation
Have somenone any idea or simple and best solution how do this better and faster ??
Making certain assumptions about the string in the buffer, one could extract all values using Regex.
Declare a global variable outside the method and compile the Regex for faster results:
Regex _regex = new Regex(#"\d+\s+\w+", RegexOptions.Compiled);
Then in your method, use the following to extract all the values:
var matches = _regex.Matches(DataToProcess)
.Cast<Match>()
.Select(m =>
{
var parts = m.Value.Split(' ');
return new { Value = parts[0], Unit = parts[1] };
});
//This is just for you to see the results in the console.
foreach (var value in matches)
{
Console.WriteLine(value);
}
Assuming DataToProcess contains this:
var DataToProcess = #"G130 g\r\nG 130 g\r\nG 124 g\r\nG 124 g\r\nG 124 g\r\n";
This would yield the following in the console:
{ Value = 130, Unit = g }
{ Value = 130, Unit = g }
{ Value = 124, Unit = g }
{ Value = 124, Unit = g }
{ Value = 124, Unit = g }
You can then use this enumeration to test for units, values, etc.
If you need the values as an array of doubles, just use:
var ProcessedData = matches.Select(m => Convert.ToDouble(m.Value)).ToArray();
Lastly, camelCase is usually preferred in C# for variable names and your code doesn't seem very thread-safe. Something to think about.

C# List not comparing with equals operator

I am comparing two lists in C#. Here is the basic code
List<string> expectedTabs = new List<string>() { "Batch", "Card Services", "ReportingXXXXX" };
List<string> actualTabs = new List<string>() { "Batch", "Card Services", "Reporting" };
string [] text = new string[actualTabs.Count];
int i = 0;
foreach (IWebElement element in actualTabs)
{
text[i++] = element;
Console.WriteLine("Tab Text : " + text[i - 1]);
}
for(int x = 0; x < actualTabs.Count; x++)
{
Assert.IsTrue(expectedTabs.Count == actualTabs.Count); //this gives equal count
Console.WriteLine(expectedTabs[x] + " :: " + actualTabs[x]);
expectedTabs[x].Equals(actualTabs[x]); //Gives wrong result here
Console.WriteLine(expectedTabs.GetType()); //returns type as collection.list
}
Now the equal should return false when comparing the last element [ReportingXXXXX and Reporting] in both the lists, but it just gives the result as equal. I am not sure if I need to use something else here.
reducing your code to what I think is the relevant bits...
var expectedTabs = new List<string>() { "Batch", "Card Services", "ReportingXXXXX" };
var actualTabs = new List<string>() { "Batch", "Card Services", "Reporting" };
for (var x = 0; x < actualTabs.Count; x++)
{
var equal = expectedTabs[x].Equals(actualTabs[x]); //Gives wrong result here
Console.WriteLine(equal);
}
This produces
True
True
False
Where are you seeing the wrong result?
Your code / comparison works fine.

Test for all possibilities in a string

I am trying to test (compare against another value in memory) for all possible combinations in a string but wanted to know what the best way is to do it.
My input string is 0246, the trick is that each number can be one of 2 options, for instance:
[0,1][2,3][4,5][6,7]
I want to be able to flip through all possible combinations, it's kind of like cracking a safe but that is not the purpose of this, I promise!
I was thinking of doing a foreach loop to switch through each option but my loops would be nested and I know performance will take a hit, since Linq is like the new black, can this be done using that?
UPDATE*
I want the result to return in order from low to high as my original string that I am comparing against could be 0001 but doesn't make sense to randomly jump around.
I also want to keep track of how many times I had to generate a different variation of this and note that down as it will be used at a later time.
Do you mean this:
var allCombinations = from a in new[] { "0", "1", }
from b in new[] { "2", "3", }
from c in new[] { "4", "5", }
from d in new[] { "6", "7", }
select a + b + c + d;
Or more fancily:
var allCombinations = from a in "01"
from b in "23"
from c in "45"
from d in "67"
select string.Concat(a, b, c, d);
In the latter (fancy) version a, b, c and d are char variables, and string.Concat(a, b, c, d) could also be written a.ToString() + b + c + d.
Here's an approach that works for any number of inputs (after parsing your format into int[][]), using Aggregate and Join:
var data = new[]
{
new[] { 0, 1 },
new[] { 2, 3 },
new[] { 4, 5 },
new[] { 6, 7 },
};
var nums = data.Aggregate(new[] { "" }.AsEnumerable(),
(agg, arr) => agg.Join(arr, x => 1, x => 1, (i, j) => i.ToString() + j));
Outputs:
0246
0247
0256
0257
0346
0347
0356
0357
1246
1247
1256
1257
1346
1347
1356
1357
It uses LINQ, so it's probably not the fastest thing ever (still < 1 ms for me with this data), and something smells about how I'm using Join with fixed join values, so it's probably not the best thing ever, but hey - it works.
I'm assuming you actually need all the values listed out. If all you're really trying to do is see if the value matches something that looks like a valid passcode, a regex like Dmitry's solution is probably the way to go.
To test the value you have im memory you don't need to generate all possible values. Generating full string and compairing it is the worse way to do this job.
If you want to control the process of compairing (count operations) you can't use any built-in methods(regular expressions or Linq). You should manually go through all the chars in string and check them.
public bool Test(string input, string[] possibleValues)
{
int operationsCount = 0;
if (input.Length != possibleValues.Length)
{
return false;
}
for (int i = 0; i < input.Length; i++)
{
bool found = false;
for (int j = 0; j < possibleValues[i].Length; j++)
{
operationsCount++;
if (input[i] == possibleValues[i][j])
{
found = true;
break;
}
}
if (!found)
{
Console.WriteLine("Value does not satisfies the condition. Number of operations: " + operationsCount);
return false;
}
}
Console.WriteLine("Value satisfies the condition. Number of operations: " + operationsCount);
return true;
}
Test("0247", new string[] { "01", "23", "45", "67" });
The shortest way is to can use Regular expression to check if the input string satisfies the condition. Regular expressions are build for matching strings to specific pattert. The only problem in this solution: you can not determine how many operations you made.
public bool Test(string input, string[] possibleValues)
{
String pattern = GeneratePattern(possibleValues);
bool result = Regex.IsMatch(input, pattern);
if (!result)
{
Console.WriteLine("Value does not satisfies the condition.");
}
else
{
Console.WriteLine("Value satisfies the condition.");
}
return result;
}
private string GeneratePattern(string[] possibleValues)
{
StringBuilder sb = new StringBuilder();
sb.Append("^");
foreach (var possibleValue in possibleValues)
{
sb.Append("[");
sb.Append(possibleValue);
sb.Append("]");
}
sb.Append("$");
return sb.ToString();
}

Help with Shannon–Fano coding

Hello I hope some one will help me with this=):
I have a set of some numbers, I need to divide them in two groups with approximately equal sum and assigning the first group with "1", second with "0", then divide each group the same way in to subgroups until subgroups will be one of number from set!
Picture explaining this crazy things):
pict
Here is the basic algorithm, if you know C# it's pretty simple to follow. And the program print the partitions as it explore the tree.
Note that there are some possible bugs and the code is light years away from the quality that a teacher would expect from an homework. (As i guess that it is an homework... if it's for work it's even worse my code would be pretty much Daily-WTF quality)
But it should allow you to understand the basic algorithm structure knowing that :
totalCount does the sum of the Count elements of the collection passed as parameter if you don't know Aggregate.
The other Aggregate usage is there for display, just ignore it.
I commented the sort as multiple elements have the same count and the C# sort function don't preserve order (And i wanted to get the same result as the wikipedia article)
The code:
var symbols = new[] {
new Symbol { Text = "A", Count=15, Probability=double.NaN, Code=""},
new Symbol { Text = "B", Count=7, Probability=double.NaN, Code="" },
new Symbol { Text = "C", Count=6, Probability=double.NaN, Code="" },
new Symbol { Text = "D", Count=6, Probability=double.NaN, Code="" },
new Symbol { Text = "E", Count=5, Probability=double.NaN, Code="" },
}.ToList();
Func<IEnumerable<Symbol>, int> totalCount =
symbols_ => symbols_.Aggregate(0, (a, s) => a + s.Count);
var total = totalCount(symbols);
foreach(var symbol in symbols)
{
symbol.Probability = total / symbol.Count;
}
//symbols.Sort((a, b) => b.Count.CompareTo(a.Count));
// Where is the Y-Combinator when you need it ?
Action<IEnumerable<Symbol>, string, int> recurse = null;
recurse = (symbols_, str, depth) => {
if (symbols_.Count() == 1)
{
symbols_.Single().Code = str;
return;
}
var bestDiff = int.MaxValue;
int i;
for(i = 1; i < symbols_.Count(); i++)
{
var firstPartCount = totalCount(symbols_.Take(i));
var secondPartCount = totalCount(symbols_.Skip(i));
var diff = Math.Abs(firstPartCount - secondPartCount);
if (diff < bestDiff) bestDiff = diff;
else break;
}
i = i - 1;
Console.WriteLine("{0}{1}|{2}", new String('\t', depth),
symbols_.Take(i).Aggregate("", (a, s) => a + s.Text + " "),
symbols_.Skip(i).Aggregate("", (a, s) => a + s.Text + " "));
recurse(symbols_.Take(i), str + "0", depth+1);
recurse(symbols_.Skip(i), str + "1", depth+1);
};
recurse(symbols, "", 0);
Console.WriteLine(new string('-', 78));
foreach (var symbol in symbols)
{
Console.WriteLine("{0}\t{1}\t{2}\t{3}", symbol.Code, symbol.Text,
symbol.Count, symbol.Probability);
}
Console.ReadLine();

Categories