I'm Learning c# and i am making some exercises. i was asked to make a program that make an array of strings and remove the vowels form it's words
i did this code to remove the vowel "S" but it didn't work. can someone help me with that ?
string[] musicinst = new string[4] { "cello", "guitar", "violin", "double bass" };
foreach (string s in musicinst)
{
if (s.Contains("s")) { s.Replace("s", ""); }
Console.WriteLine(s);
}
now this code outputs the words exactly as i typed them in the array with no changes. so what is the problem here ?
.Replace does not change the string but returns a new string with the change. You need to now assign it back to s:
if (s.Contains("s"))
{
s = s.Replace("s", "o");
}
This will now also not work:
Cannot assign to 's' because it is a 'foreach iteration variable'
So instead use a for loop and access by indexer or create a new list and add the result of s.Replace to it:
string[] musicinst = new string[4] { "cello", "guitar", "violin", "double bass" };
var newData = musicinst.Select(item => item.Replace("s", "o")).ToArray();
If you need to deal with replacement when insensitive then look at:
Is there an alternative to string.Replace that is case-insensitive?
You're running into a feature of C# strings called immutability - operations on strings do not change the string, it returns a new string. given this, you might think you need to do this:
s = s.Replace("s", "o");
But that won't work because 's' is a foreach iterator. Your best bet is to recast your loop:
for (int i = 0; i < musicinst.Length; ++i)
{
if (musicinst[i].Contains("s"))
{
musicinst[i] = musicinst.Replace("s", "o");
}
}
Which will change your array in-place. To preserve immutability of the array as well you might consider a LINQ-like option that builds a new array as others have demonstrated.
static void Main(string[] args)
{
string[] musicinst = new string[4] { "cello", "guitar", "violin", "double bass" };
char[] vowels = new char[5] { 'a', 'e', 'i' ,'o', 'u' };
List<string> output = new List<string>();
foreach (string s in musicinst)
{
string s1 = s;
foreach (var v in vowels)
{
if (s1.Contains(v))
{
s1=s1.Remove(s1.IndexOf(v),1);
}
}
output.Add(s1);
}
Console.ReadLine();
}
Whenever you perform any operation on a string data type, it creates a new string which you have to store in a new variable.
Related
I am trying to append two StringBuilders so that they produce something like:
Device # 1 2 3
Pt.Name ABC DEF GHI
what I have tried is:
class Program
{
class Device
{
public int ID { get; set; }
public string PatName { get; set; }
}
static void Main(string[] args)
{
var datas = new List<Device>
{
new Device { ID = 1, PatName = "ABC" },
new Device { ID = 2, PatName = "DEF" },
new Device { ID = 3, PatName = "GHO" }
};
// there is a collection which has all this information
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0} {1}", "Device #", "Pt.Name").AppendLine();
foreach (var data in datas)
{
var deviceId = data.ID;
var patName = data.PatName;
sb.AppendFormat("{0} {1}", deviceId, patName).AppendLine();
}
Console.WriteLine(sb);
}
}
but it is printing it in vertical manner, like
Device # Pt.Name
1 ABC
2 DEF
3 GHI
and if I remove that last AppendLine(); it is appending it at the end in the same line.
I want to use only one stringbuilder followed by only one foreach loop.
1.You could do it like:
StringBuilder sb=new StringBuilder();
sb.Append("Device #");
foreach(var data in datas)
sb.Append($" {data.deviceId}");
sb.Append("PT.Name");
foreach(var data in datas)
sb.Append($" {data.PatName}");
2.if you want to loop only once then you can use 2 StringBuilders:
StringBuilder sb1=new StringBuilder();
StringBuilder sb2=new StringBuilder();
sb1.Append("Device #");
sb2.Append("Pt.Name");
foreach(var data in datas)
{
sb1.Append($" {data.deviceId}");
sb2.Append($" {data.patName}");
}
sb1.Append(sb2.ToString());
3.You could also use string.Join() which also relies on StringBuilder to write a one-liner but however this way you have extra select statements:
string result = $"Device # {string.Join(" ",datas.Select(x => x.deviceId))}\r\nPt.Name {string.Join(" ",datas.Select(x => x.patName))}";
I love your question because it is based on avoiding these two assumption, 1) that strings are always printed left to right and 2) newlines always result in advancing the point of printing downwards.[1]
Others have given answers that will probably meet your needs, but I wanted to write about why your way of thinking won’t work. The assumptions above are so engrained into people’s thinking about how strings and terminals work that I'm sure many people taught your question was odd or even naïve, I did at first.
StringBuilder doesn’t print strings to the screen. Somewhere I suspect you are calling Console.Write to print the string. StringBuilder allows you to convert non-string variables as strings and to concatenate strings together in a more efficient way than String.Format and the + operator, see Immutability and the StringBuilder class.
When you are done using StringBuilder what you have is a string of characters. It’s called a string because it is a 1D structure, one character after each other. There is nothing special about the new line characters in the string,[2] they are just characters in the list. There is nothing in the string that specifies the position the characters other that that each one comes after the previous one. When you do something like Console.Write the position of the character on the screen is defined by the implementation of that method, or the implements of the terminal, or both. They follow the conventions of our language, i.e. each character is to the right of the previous one. When Console.Write you encounters a newline it then prints the following character in the first position of the line below the current one.
If you are using String, StringBuilder and Console you can write code to create a single string with the pieces of test in the places you want so that when Console.Write follows the left to write, top to bottom conventions your text will appear correctly. This is what the other answers here do.
Alternately you could find a library which gives you more control over when text is printed on the screen. These were very popular before Graphical User Interfaces when people build interactive applications in text terminals. Two examples I remember are CRT for Pascal and Ncurses for C. If you want to investigate this approach I’d suggest doing some web searches or even another question here. Most terminal applications you see at banks, hospitals and airlines use such a library running on a VAX.
[1] This may be differently in systems setup for languages which are not like English, or not like Latin.
[2] The character or characters which reprsent a new line are different on different operating systems.
normally you cannot horizontally append to the right side of stringbuilder so maybe you roll your own extension method such as
static class SbExtensions
{
public static StringBuilder AppendRight(this StringBuilder sb, string key, string value)
{
string output = sb.ToString();
string[] splitted = output.Split("\n");
splitted[0] += key.PadRight(10, ' ');
splitted[1] += value.PadRight(10, ' ');
output = string.Join('\n', splitted);
return new StringBuilder(output);
}
}
simple solution:
StringBuilder sb2 = new StringBuilder(columns);
foreach(var data in datas)
{
sb2 = sb.AppendRight(data.ID.ToString(), data.PatName);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
complex one: dynamic
just another solution using MathNet.Numerics library at https://numerics.mathdotnet.com/
introduce an array property in your Entity class
class Device
{
public int ID { get; set; }
public string PatName { get; set; }
public string[] Array
{
get
{
return new string[] { ID.ToString(), PatName };
}
}
}
then in main method
static void Main(string[] args)
{
var datas = new List<Device>
{
new Device { ID = 1, PatName = "ABC" },
new Device { ID = 2, PatName = "DEF" },
new Device { ID = 3, PatName = "GHO" }
};
var MatrixValues = datas
.SelectMany(x => x.Array)
.Select((item, index) => new KeyValuePair<double, string>(Convert.ToDouble(index), item)).ToArray();
var matrixIndexes = MatrixValues.Select(x => x.Key);
var M = Matrix<double>.Build;
var C = M.Dense(datas.Count, datas.First().Array.Count(), matrixIndexes.ToArray());
var TR = C.Transpose();
string columns = "Device #".PadRight(10, ' ') + "\n" + "Pt.Name".PadRight(10, ' ');
StringBuilder sb = new StringBuilder(columns);
for (int i = 0; i < TR.Storage.Enumerate().Count(); i += 2)
{
sb = sb.AppendRight(MatrixValues[i].Value, MatrixValues[i + 1].Value);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
}
yea and those references
using MathNet.Numerics.LinearAlgebra;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Output
PS: this may not be your desired solution as it is creating multiple string builders when you append new data
This should get you going:
Code
StringBuilder deviceSB = new StringBuilder();
StringBuilder patNameSB = new StringBuilder();
deviceSB.Append("Device #".PadRight(9));
patNameSB.Append("Pt.Name".PadRight(9));
foreach (var data in datas)
{
deviceSB.Append($"{data.Device}".PadLeft(2).PadRight(4));
patNameSB.Append($"{data.PatName} ");
}
deviceSB.AppendLine();
deviceSB.Append(patNameSB);
Or optional without loop
StringBuilder result = new StringBuilder();
StringBuilder s1 = new StringBuilder("Device # ".PadRight(9));
StringBuilder s2 = new StringBuilder("Pt.Name ".PadRight(9));
s1 = s1.AppendJoin(String.Empty, datas.Select(x => x.Device.PadLeft(2).PadRight(4)));
s2 = s2.AppendJoin(' ', datas.Select(x => x.PatName));
result = result.Append(s1).AppendLine().Append(s2);
Note that i took the idea of the second option from #AshkanMobayenKhiabani, but instead of using strings i stick to StringBuilder since it is much more performant than using strings!
Output
Both of previous options offer the same output:
Please read the specifics below:
I have a string array as so:
string[] data = new string[] { "word1", "word2", "word3" };
I'm trying to put a STRING variable, which will include the "word1", "word2", "word3" data, inside of the string array.
In other words, I want the string array to look as so:
SingleStringHere = "\"word1\", \"word2\", \"word3\"";
string[] data = new string[] { SingleStringHere };
The 'SingleStringHere' variable will be retrieving data off of a server, which will be used in the string array. The string array will be formatted and encrypted properly, in order to be sent in a packet through a socket.
No errors are given with the code, however, data in the 'SingleStringHere' variable is not being read as separate strings. I do NOT want to put the retrieved server data into a string array, because that will be TOO MUCH parsing!
If the strings that you receive don't contain commas, you could do something as simple as this:
string SingleStringHere = "\"word1\", \"word2\", \"word3\"";
string[] data = SingleStringHere.Replace("\"").Split(',');
Otherwise, you're going to have to do some more complex parsing. Something like this:
static void Main(string[] args)
{
string SingleStringHere = "\"word1\", \"word2\", \"word3\"";
string[] data = ParseSingleString(SingleStringHere);
foreach(string s in data)
{
Console.WriteLine(s);
}
}
public static string[] ParseSingleString(string singleString)
{
List<string> multipleStrings = new List<string>();
StringBuilder current = new StringBuilder();
bool inQuote = false;
for(int index = 0; index < singleString.Length; ++index) // iterate through the string
{
if (singleString[index] == '"')
{
inQuote = !inQuote;
}
else if (!inQuote && singleString[index] == ',') // split at comma if not in quote
{
multipleStrings.Add(current.ToString().Trim());
current.Clear();
}
else
{
current.Append(singleString[index]);
}
}
multipleStrings.Add(current.ToString()); // don't forget the last one
return multipleStrings.ToArray();
}
If the strings can contain quotes, it can get trickier. That's just my rough example.
Be warned that this operation might be memory-intensive with all of the string copying and so forth (I count about 3 copies for each substring). You may be able to circumvent some of this by recording the indices of the first and last character in one of the strings, and then taking a substring on the entire singleString. Also note that List<string> has to be copied into an array before it returns. You may want to just return an IEnumerable<string> instead, or even an IList<string>. But it's late, and I think the above is sufficient for this question.
P.s. I'm on a Linux machine right now without access to a C# compiler, so I apologize for any typos.
You can get array from formatted string like this
string SingleStringHere = "\"word1\", \"word2\", \"word3\"";
String[] arr = SingleStringHere.Split(',');
Suppose I have written "5 and 6" or "5+6". How can I assign 5 and 6 to two different variables in c# ?
P.S. I also want to do certain work if certain chars are found in string. Suppose I have written 5+5. Will this code do that ?
if(string.Contains("+"))
{
sum=x+y;
}
string input="5+5";
var numbers = Regex.Matches(input, #"\d+")
.Cast<Match>()
.Select(m => m.Value)
.ToList();
Personally, I would vote against doing some splitting and regular expression stuff.
Instead I would (and did in the past) use one of the many Expression Evaluation libraries, like e.g. this one over at Code Project (and the updated version over at CodePlex).
Using the parser/tool above, you could do things like:
A simple expression evaluation then could look like:
Expression e = new Expression("5 + 6");
Debug.Assert(11 == e.Evaluate());
To me this is much more error-proof than doing the parsing all by myself, including regular expressions and the like.
You should use another name for your string than string
var numbers = yourString.Split("+");
var sum = Convert.ToInt32(numbers[0]) + Convert.ToInt32(numbers[1]);
Note: Thats an implementation without any error checking or error handling...
If you want to assign numbers from string to variables, you will have to parse string and make conversion.
Simple example, if you have text with only one number
string text = "500";
int num = int.Parse(text);
Now, if you want to parse something more complicated, you can use split() and/or regex to get all numbers and operators between them. Than you just iterate array and assign numbers to variables.
string text = "500+400";
if (text.Contains("+"))
{
String[] data = text.Split("+");
int a = int.Parse(data[0]);
int b = int.Parse(data[1]);
int res = a + b;
}
Basicly, if you have just 2 numbers and operazor between them, its ok. If you want to make "calculator" you will need something more, like Binary Trees or Stack.
Use the String.Split method. It splits your string rom the given character and returns a string array containing the value that is broken down into multiple pieces depending on the character to break, in this case, its "+".
int x = 0;
int y = 0;
int z = 0;
string value = "5+6";
if (value.Contains("+"))
{
string[] returnedArray = value.Split('+');
x = Convert.ToInt32(returnedArray[0]);
y = Convert.ToInt32(returnedArray[1]);
z = x + y;
}
Something like this may helpful
string strMy = "5&6";
char[] arr = strMy.ToCharArray();
List<int> list = new List<int>();
foreach (char item in arr)
{
int value;
if (int.TryParse(item.ToString(), out value))
{
list.Add(item);
}
}
list will contains all the integer values
You can use String.Split method like;
string s = "5 and 6";
string[] a = s.Split(new string[] { "and", "+" }, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine(a[0].Trim());
Console.WriteLine(a[1].Trim());
Here is a DEMO.
Use regex to get those value and then switch on the operand to do the calculation
string str = "51 + 6";
str = str.Replace(" ", "");
Regex regex = new Regex(#"(?<rightHand>\d+)(?<operand>\+|and)(?<leftHand>\d+)");
var match = regex.Match(str);
int rightHand = int.Parse(match.Groups["rightHand"].Value);
int leftHand = int.Parse(match.Groups["leftHand"].Value);
string op = match.Groups["operand"].Value;
switch (op)
{
case "+":
.
.
.
}
Split function maybe is comfortable in use but it is space inefficient
because it needs array of strings
Maybe Trim(), IndexOf(), Substring() can replace Split() function
Is there any method that I can use that returns a fixed length array after spliting a string with some delimiter and fill the rest with a default string.
Eg.
string fullName = "Jhon Doe";
string[] names = fullName.SpecialSplit(some parameters); //This should always return string array of length 3 with the second elememnt set to empty if there is no middle name.
Next time specify the language you're asking about. We're no guessers.
In Java:
fullName.split(" ");
And anyway, no method will "return string array of length 3 with the second elememnt set to empty if there is no middle name". For the method, there are just two elements. You have to write that method yourself wrapping the standard split() method.
You should read over Jon Skeet's Writing the perfect question. It will be beneficial to you in the future when posting questions of StackOverflow.
There is no method in C# to do what you are asking, but you can easily write an extension method to do what I think you are asking.
here is a quick example:
public static class AbreviatorExtention
{
public static string[] GetInitials(this String str, char splitChar)
{
string[] initialArray = new string[3];
var nameArray = str.Split(new char[] { splitChar },
StringSplitOptions.RemoveEmptyEntries);
if (nameArray.Length == 2)
{
var charArrayFirstName = nameArray[0].ToCharArray();
var charArrayLastName = nameArray[1].ToCharArray();
initialArray[0] = charArrayFirstName[0].ToString().ToUpper();
initialArray[1] = string.Empty;
initialArray[2] = charArrayLastName[0].ToString().ToUpper();
}
else
{
for (int i = 0; i < nameArray.Length; i++)
{
initialArray[i] = (nameArray[i].ToCharArray())[1]
.ToString().ToUpper();
}
}
return initialArray;
}
}
class Program
{
static void Main(string[] args)
{
string FullName = "john doe";
//Extension method in use
string[] names = FullName.GetInitials(' ');
foreach (var item in names)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
Output:
J
D
I would set it up to split the string separate from the fixed array. If you still want a fixed array, then you set up the array to a size of three an populate. This is not the best method, however, as it has no meaning. Better, set up a person or user class and then populate, via rules, from the split string.
How can I remove duplicate substrings within a string? so for instance if I have a string like smith:rodgers:someone:smith:white then how can I get a new string that has the extra smith removed like smith:rodgers:someone:white. Also I'd like to keep the colons even though they are duplicated.
many thanks
string input = "smith:rodgers:someone:smith:white";
string output = string.Join(":", input.Split(':').Distinct().ToArray());
Of course this code assumes that you're only looking for duplicate "field" values. That won't remove "smithsmith" in the following string:
"smith:rodgers:someone:smithsmith:white"
It would be possible to write an algorithm to do that, but quite difficult to make it efficient...
Something like this:
string withoutDuplicates = String.Join(":", myString.Split(':').Distinct().ToArray());
Assuming the format of that string:
var theString = "smith:rodgers:someone:smith:white";
var subStrings = theString.Split(new char[] { ':' });
var uniqueEntries = new List<string>();
foreach(var item in subStrings)
{
if (!uniqueEntries.Contains(item))
{
uniqueEntries.Add(item);
}
}
var uniquifiedStringBuilder = new StringBuilder();
foreach(var item in uniqueEntries)
{
uniquifiedStringBuilder.AppendFormat("{0}:", item);
}
var uniqueString = uniquifiedStringBuilder.ToString().Substring(0, uniquifiedStringBuilder.Length - 1);
Is rather long-winded but shows the process to get from one to the other.
not sure why you want to keep the duplicate colons. if you are expecting the output to be "smith:rodgers:someone::white" try this code:
public static string RemoveDuplicates(string input)
{
string output = string.Empty;
System.Collections.Specialized.StringCollection unique = new System.Collections.Specialized.StringCollection();
string[] parts = input.Split(':');
foreach (string part in parts)
{
output += ":";
if (!unique.Contains(part))
{
unique.Add(part);
output += part;
}
}
output = output.Substring(1);
return output;
}
ofcourse i've not checked for null input, but i'm sure u'll do it ;)