I am using this Linq to find the highest int in a list, so that I can increment it and add it to the end of the next string:
var CableNumber = CList.Select(v => int.Parse(v.CableNumber.Substring(n))).Max();
However, because the strings aren't a fixed length, I was thinking of inserting a Regex.Match in there, possibly something like:
n = Regex.Match(CableNumber, #"\d{3}", RegexOptions.RightToLeft);
To specify; the only format the input strings follow is that it will always have a 3 digit number on the end, possibly followed by a single letter. Some examples:
CP1-P-CP2-001 (001)
MOT1PSP2023A (023)
TKSP3-C-FLT2-234-A (234)
How could I implement this? Is there a better way?
The following uses the regex pattern inside the linq query:
string[] strings = { "CP1-P-CP2-001 (001)","MOT1PSP2023A (023)", "TKSP3-C-FLT2-234-A (234)",
"InvalidString" };
int? maxValue = strings.Max(x =>
{
var match = Regex.Match(x, #"\d{3}(?=\D*$)");
return match.Success ? (int?) int.Parse(match.Value) : null;
});
The int? is so we can bypass any string.Empty coming back from an invalid match and only parse valid matches. If none matched, will return null.
How about this?
var CableNumber = CList.Select(v =>
int.Parse(v.CableNumber.Substring(v.CableNumber.Length - 3))).Max();
Or to be safe (to prevent string that is less than 3 characters).
var CableNumber = CList.Select(v =>
int.Parse(("000" + v.CableNumber).Substring(("000" + v.CableNumber).Length - 3))).Max();
update
Use LastIndexOfAny
var CableNumber = CList.Select(v =>
int.Parse(v.CableNumber.Substring(v.CableNumber
.LastIndexOfAny("0123456789".ToCharArray()) - 2, 3))).Max();
You can use the next regex to get last 3 digits in each string:
(\d{3})\D*$
Related
I am trying to parse a string into array and find a very concise approach.
string line = "[1, 2, 3]";
string[] input = line.Substring(1, line.Length - 2).Split();
int[] num = input.Skip(2)
.Select(y => int.Parse(y))
.ToArray();
I tried remove Skip(2) and I cannot get the array because of non-int string. My question is that what is the execution order of those LINQ function. How many times is Skip called here?
Thanks in advance.
The order is the order that you specify. So input.Skip(2) skips the first two strings in the array, so only the last remains which is 3. That can be parsed to an int. If you remove the Skip(2) you are trying to parse all of them. That doesn't work because the commas are still there. You have splitted by white-spaces but not removed the commas.
You could use line.Trim('[', ']').Split(','); and int.TryParse:
string line = "[1, 2, 3]";
string[] input = line.Trim('[', ']').Split(',');
int i = 0;
int[] num = input.Where(s => int.TryParse(s, out i)) // you could use s.Trim but the spaces don't hurt
.Select(s => i)
.ToArray();
Just to clarify, i have used int.TryParse only to make sure that you don't get an exception if the input contains invalid data. It doesn't fix anything. It would also work with int.Parse.
Update: as has been proved by Eric Lippert in the comment section using int.TryParse in a LINQ query can be harmful. So it's better to use a helper method that encapsulates int.TryParse and returns a Nullable<int>. So an extension like this:
public static int? TryGetInt32(this string item)
{
int i;
bool success = int.TryParse(item, out i);
return success ? (int?)i : (int?)null;
}
Now you can use it in a LINQ query in this way:
string line = "[1, 2, 3]";
string[] input = line.Trim('[', ']').Split(',');
int[] num = input.Select(s => s.TryGetInt32())
.Where(n => n.HasValue)
.Select(n=> n.Value)
.ToArray();
The reason it does not work unless you skip the first two lines is that these lines have commas after ints. Your input looks like this:
"1," "2," "3"
Only the last entry can be parsed as an int; the initial two will produce an exception.
Passing comma and space as separators to Split will fix the problem:
string[] input = line
.Substring(1, line.Length - 2)
.Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries);
Note the use of StringSplitOptions.RemoveEmptyEntries to remove empty strings caused by both comma and space being used between entries.
I think it would be better you do it this way:
JsonConvert.DeserializeObject(line, typeof(List<int>));
you might try
string line = "[1,2,3]";
IEnumerable<int> intValues = from i in line.Split(',')
select Convert.ToInt32(i.Trim('[', ' ', ']'));
I have string like this:
strings s = "1.0E-20"
Is there a way to get only -20 from this using regex?
I tried this:
(([1-9]+\.[0-9]*)|([1-9]*\.[0-9]+)|([1-9]+))([eE][-+]?[0-9]+)?
this gets me e-20 in group5 but still not just -20.
Use Regex for dealing with text, use Math(s) for dealing with numbers:
Math.Log10(Convert.ToDouble("1.0E-20")) // returns -20
To make sure your string input is a valid double use TryParse:
double d, result = 0.0;
if (Double.TryParse("1.0E-20", out d))
{
result = Math.Log10(d);
}
else
{
// handle error
}
Also, if you want to get the 1.0 (multiplier) from your input:
var d = Convert.ToDouble("1.0E-20");
var exponent = Math.Log10(d);
var multiplier = d / exponent;
No need for Regex when string methods can do wonders
string str = "1.0E-20";
str = str.Substring(str.IndexOf('E') + 1);
You can do that without Regex like:
string s = "1.0E-20";
string newStr = s.Substring(s.IndexOf('E') + 1);
Later you can parse the string to number like:
int number;
if (!int.TryParse(newStr, out number))
{
//invalid number
}
Console.WriteLine(number);
You can also use string.Split like:
string numberString = s.Split('E')[1]; //gives "-20"
Its better if you add check for string/array length when access string.Substring or accessing element 1 after split.
var x = str.IndexOf("E") != -1 ? str.Substring(str.IndexOf("E") + 1) : "1";
If you want to use regular expressions to achieve this, you should switch up your capture groups.
(([1-9]+\.[0-9]*)|([1-9]*\.[0-9]+)|([1-9]+))([eE])([-+]?[0-9]+)?
Group 6 will contain -20 with your given example with the regular expression above. Note how the parentheses have moved. We might need more information from you though. Do you have any more sample data? What's the end goal here?
How to sort this?
I have List of string with values like this
11-03-2013
11-03-2013 -Count=2
11-03-2013 -count=1
11-04-2013 -Count=1
11-04-2013 -Count=2
11-04-2013
Output should be, The one without the count should be on the last and the top most should be 1 followed by 1 and dates should be ordered by ascending.
11-03-2013 -Count=2
11-03-2013 -count=1
11-03-2013
11-04-2013 -Count=2
11-04-2013 -Count=1
11-04-2013
I tried this code but this is sorting this by descending
var edates= edates.OrderBy(e => e.Replace("-count=1", string.Empty).Replace("-count=2", string.Empty)).ToList();
I know that a simple class with properties can do the trick but doing that so would need to change other methods which would require a lot of work.
Regards
it is because you compare strings not dates. Create two functions: first substrings date part and parses it and second substrings the count part and returns parsed count(or 0 if length < 11) and then yourList.OrderBy(s=> f1(s)).ThenByDescending(s=> f2(s))
Here is #Guru Stron's solution in code
private static void sortList()
{
var dates = getDates();
var sorted = dates.OrderBy(f1).ThenByDescending(f2);
}
private static DateTime f1(string parse)
{
return DateTime.Parse(parse.Substring(0, 10));
}
private static int f2(string parse)
{
int sort;
if (parse.Length > 10) int.TryParse(parse.Substring(18), out sort);
else sort = 0;
return sort;
}
You first need to order by the date, then by the rest of the string, descending;
edates =
edates.OrderBy(x =>
DateTime.ParseExact(x.Substring(0, 10), "MM-dd-yyyy",
CultureInfo.InvariantCulture))
.ThenByDescending(x => x.Substring(10))
.ToList();
You ought to use a real class to simplify things and have a proper representation. That said, you can use the LINQ query syntax and take advantage of the let clause to store the result of splitting the text on a space and the equal symbol. If the split result has more than one element we can assume the count exists. Next, we order by the date (after parsing it) and then by parsing the count (descending).
Try this approach out:
string[] inputs =
{
"11-03-2013",
"11-03-2013 -Count=2",
"11-03-2013 -Count=1",
"11-04-2013 -Count=1",
"11-04-2013 -Count=2",
"11-04-2013"
};
var query = from input in inputs
let split = input.Split(' ', '=')
let count = split.Length > 1 ? int.Parse(split[2]) : 0
orderby DateTime.Parse(split[0]), count descending
select input;
foreach (var item in query)
{
Console.WriteLine(item);
}
The following should order as desired across different years and counts >= 10.
This won't be the "fastest" method (and it will most certainly not work with L2S/EF LINQ providers), but it should sort the given format correctly and fail-fast on invalid values. I would likely write the code like this (or use an IComparer with an equivalent setup) - mainly because it "reads simple" to me.
DateTime OrderableItem (string e) {
var date = e.Substring(0, 10);
return DateTime.ParseExact(date, "MM-dd-yyyy", CultureInfo.InvariantCulture)
}
int OrderableCount (string e) {
var m = Regex.Match(e, #"-count=(\d+)$", RegexOptions.IgnoreCase);
return m.Success
? int.Parse(m.Groups[1].Value)
: 0;
}
var res = seq.OrderBy(OrderableDate)
.ThenBy(OrderableCount);
I much prefer to take data, instead of excluding data.
I've got a scenario, wherein i need to replace the string literal with new text.
For example, if my string is "01HW128120", i will first check if the text contains "01HW" If yes, then replace that with the string "MachineID-".
So eventually i wanted "01HW128120" to be "MachineID-128120". Sometimes i do get the string as "1001HW128120" - In this case also i require to replace the "1001HW" with "MachineID-"
I tried the below code snippet, but this does not work to my expectation.
string sampleText = "01HW128120";
if(sampleText.Contains("01HW"))
sampleText = sampleText.Replace("01HW","MachineID-");
Any suggestion would be of great help to me.
Few Possible Search Values
If there are only a few possible combinations, you can simply do multiple tests:
string value = "01HW128120";
string replacement = "MachineID-";
if( value.Contains( "01HW" ) ) {
value = value.Replace( "01HW", replacement );
}
else if( value.Contains( "1001HW" ) ) {
value = value.Replace( "1001HW", replacement );
}
Assert.AreEqual( "MachineID-128120", value );
Many Possible Search Values
Of course, this approach quickly becomes unwieldy if you have a large quantity of possibilities. Another approach is to keep all of the search strings in a list.
string value = "01HW128120";
string replacement = "MachineID-";
var tokens = new List<string> {
"01HW",
"1001HW"
// n number of potential search strings here
};
foreach( string token in tokens ) {
if( value.Contains( token ) ) {
value = value.Replace( token, replacement );
break;
}
}
"Smarter" Matching
A regular expression is well-suited for string replacement if you have a manageable number of search strings but you perhaps need not-exact matches, case-insensitivity, lookaround, or capturing of values to insert into the replaced string.
An extremely simple regex which meets your stated requirements: 1001HW|01HW.
Demo: http://regexr.com?34djm
A slightly smarter regex: ^\d{2,4}HW
Assert position at start of string
Match 2-4 digits
Match the value "HW" literally
See also: Regex.Replace Method
If you just want to replace everything up to "01HW" with "MachineID-", you could use a generic regex:
sampleText = Regex.Replace(sampleText, "^.*01HW", "MachineID-");
I have a numeric string like this 2223,00. I would like to transform it to 2223. This is: without the information after the ",". Assume that there will be only two decimals after the ",".
I did:
str = str.Remove(str.Length - 3, 3);
Is there a more elegant solution? Maybe using another function? -I donĀ“t like putting explicit numbers-
You can actually just use the Remove overload that takes one parameter:
str = str.Remove(str.Length - 3);
However, if you're trying to avoid hard coding the length, you can use:
str = str.Remove(str.IndexOf(','));
Perhaps this:
str = str.Split(",").First();
This will return to you a string excluding everything after the comma
str = str.Substring(0, str.IndexOf(','));
Of course, this assumes your string actually has a comma with decimals. The above code will fail if it doesn't. You'd want to do more checks:
commaPos = str.IndexOf(',');
if(commaPos != -1)
str = str.Substring(0, commaPos)
I'm assuming you're working with a string to begin with. Ideally, if you're working with a number to begin with, like a float or double, you could just cast it to an int, then do myInt.ToString() like:
myInt = (int)double.Parse(myString)
This parses the double using the current culture (here in the US, we use . for decimal points). However, this again assumes that your input string is can be parsed.
String.Format("{0:0}", 123.4567); // "123"
If your initial value is a decimal into a string, you will need to convert
String.Format("{0:0}", double.Parse("3.5", CultureInfo.InvariantCulture)) //3.5
In this example, I choose Invariant culture but you could use the one you want.
I prefer using the Formatting function because you never know if the decimal may contain 2 or 3 leading number in the future.
Edit: You can also use Truncate to remove all after the , or .
Console.WriteLine(Decimal.Truncate(Convert.ToDecimal("3,5")));
Use:
public static class StringExtensions
{
/// <summary>
/// Cut End. "12".SubstringFromEnd(1) -> "1"
/// </summary>
public static string SubstringFromEnd(this string value, int startindex)
{
if (string.IsNullOrEmpty(value)) return value;
return value.Substring(0, value.Length - startindex);
}
}
I prefer an extension method here for two reasons:
I can chain it with Substring.
Example: f1.Substring(directorypathLength).SubstringFromEnd(1)
Speed.
You could use LastIndexOf and Substring combined to get all characters to the left of the last index of the comma within the sting.
string var = var.Substring(0, var.LastIndexOf(','));
You can use TrimEnd. It's efficient as well and looks clean.
"Name,".TrimEnd(',');
Try the following. It worked for me:
str = str.Split(',').Last();
Since C# 8.0 it has been possible to do this with a range operator.
string textValue = "2223,00";
textValue = textValue[0..^3];
Console.WriteLine(textValue);
This would output the string 2223.
The 0 says that it should start from the zeroth position in the string
The .. says that it should take the range between the operands on either side
The ^ says that it should take the operand relative to the end of the sequence
The 3 says that it should end from the third position in the string
Use lastIndexOf. Like:
string var = var.lastIndexOf(',');