"abc_d1.txt" should get 0
"abc_d1_2.txt" should get 2
"abc_d1_14.txt" should get 14
"abc_d12_x2_156.txt" should get 156
Can do this currently with regex
int Id;
string pattern = #"(?<=_)\d+(?=\.\w+)|(?<!_\d+)(?=\.\w+)";
Regex rg = new Regex(pattern);
var z = rg.Match(fullFileName).Value;
Id = z == "" ? 0 : Convert.ToInt32(z);
But how to do it without regex?
I'm not sure why you don't want to use regex (I don't judge). However, assuming everything is exactly as you have described, and you need no validation. A low tech approach would be to just Split the string, TryParse the results, then call LastOrDefault
Given
private static int GetNumber(string input)
=> input.Split('_','.')
.Select(x => int.TryParse(x, out var value) ? value : (int?)null)
.LastOrDefault(x => x != null) ?? 0;
Usage
var asd = new List<string>()
{
"abc_d1.txt",
"abc_d1_2.txt",
"abc_d1_14.txt",
"abc_d12_x2_156.txt"
};
foreach (var result in asd.Select(GetNumber))
Console.WriteLine(result);
Output
0
2
14
156
Classic string manipulation:
int u = s.LastIndexOf('_');
return int.TryParse(s.Substring(u + 1, s.Length - u - 5), out int i) ? i : 0;
(Following on from the comment) - the 5 is a bit of "magic number" I guess - it's the length of the extension ".txt" 4 plus 1 to negate the effect of u being one index short (because it's pointing to the position of the underscore, not the position of the first char of the number after it). I could have written s.Substring(u + 1, s.Length - (u + 1) - 4 but this form might self-explain a little better:
int begin = s.LastIndexOf('_') + 1;
return int.TryParse(s.Substring(begin, s.Length - begin - 4), out int i) ? i : 0;
Related
Let's say I have a list of ints in c# like this:
List<int> numbers = new List<int>() { 1, 2, 7, 20, 3 };
Is there a way of checking that it has alternating odd and even numbers in it (like in the example above: if one if them is even then the next must be odd or vice versa)?
I know it's simple to check it in a loop, but I'm trying to implement this using LINQ and extension methods only.
Let's analyze the problem. What does it mean alternating parity?
index : value : value + index
------------------------------
0 : 1 : 1 - note, all sums are odd
1 : 2 : 3
2 : 7 : 9
....
Or
index : value : value + index
------------------------------
0 : 2 : 2 - note, all sums are even
1 : 1 : 2
2 : 6 : 8
....
As you can see (and you can easily prove it) alternating parity means that
index + value sums are either all odd or all even. Let's check it with a help of Linq:
List<int> numbers = new List<int>() { 1, 2, 7, 20, 3, 79 };
bool result = numbers
.DefaultIfEmpty()
.Select((item, index) => Math.Abs((long)item + (long)index))
.Aggregate((s, a) => s % 2 == a % 2 ? s % 2 : -1) >= 0;
Notes to the implementation:
DefaultIfEmpty() - empty sequence has all (all zero) values alternating; however, Aggregate has nothing to aggregate and throws exception. Let's turn empty sequence into one element sequence.
(long) in order to prevent integer overflow (int.MaxValue + index can well be out of int range)
Math.Abs: c# can return negative remainder (e.g. -1 % 2); we don't want an additional check for this, so let's work with absolute values
However, we can exploit this effect (-1 % 2 == -1) in the final Aggregate
Extension Method solution I hope is easier to understand:
public static bool IsAlternating(this IEnumerable<int> source) {
if (null == source)
throw new ArgumentNullException(nameof(source));
bool expectedZero = false;
bool first = true;
foreach (int item in source) {
int actual = item % 2;
if (first) {
first = false;
expectedZero = actual == 0;
}
else if (actual == 0 && !expectedZero || actual != 0 && expectedZero)
return false;
expectedZero = !expectedZero;
}
return true;
}
Note, that the loop solution (extension method) is more efficient: it returns false immediately when pattern doesn't meet.
You can do with LINQ in this way. you can check if there is atelast one even item at add place or an odd item at even place.
List<int> numbers = new List<int>() { 1, 2, 7, 20, 3 };
var temp = numbers.Where((x, i) => (i % 2 == 0 && x % 2 == 0) || (i % 2 == 1 && x % 2 == 1)).Take(1);
int count = temp.Count();
if(count == 0)
{
//true
}
else
{
//false
}
Note: Assuming that you are expecting even numbers at even place and odd numbers at an odd place.
You can use Aggregate to determine if a sequence is alternating.
Let's assume that if there's 0 or 1 elements then the result is true.
You can modify this logic however you see fit.
Based on this an extension method can be created:
public static bool IsAlternatingParitySequenceVerbose(this IEnumerable<int> col)
{
// state:
// -1 - sequence is not alternating
// 0 - even
// 1 - odd
if (!col.Any())
return true;
//check if first value is even
var firstState = Math.Abs(col.First() % 2);
var IsAlternating = col.Skip(1).Aggregate(firstState, (state, val) =>
{
if (state == -1)
return -1;
var current = Math.Abs(val % 2);
if (current == state)
return -1;
return current;
});
return IsAlternating != -1;
}
And then make a one-liner out of it:
public static bool IsAlternatingParitySequence(this IEnumerable<int> col) =>
!col.Any()
|| col.Skip(1).Aggregate(Math.Abs(col.First() % 2), (state, val) =>
state == -1
? -1
: Math.Abs(val % 2) is var current && current == state
? -1
: current
) != -1;
I have an List of strings that I will pull from an enum and I need to sort in alphabetical order then by number and some strings won't have numbers, those strings should be at the end of the list.
List =
"Incl 11
Incl 12
Excl 4
Incl 3
Other
Incl 4
Incl 10
Incl 11
Excl 10
Incl 1
Incl 2
Withdrew Col
Excl 1
Excl 2
Excl 3
Follow Up
"
So far I have this but it's only sorted by number and not alphabetically first, any help would be appreciated.
var test = Enum.GetValues(typeof(Reason))
.Cast<Reason>()
.Select(sc => new SelectListItem { Value = ((int)sc).ToString(), Text = sc.GetDisplayName() })
.OrderBy(s => s.Text, new MyNumberComparer())
.ToList();
class MyNumberComparer : IComparer<string>
{
public int Compare(string x, string y)
{
var xResultString = Regex.Match(x, #"\d+").Value;
var yResultString = Regex.Match(y, #"\d+").Value;
int xVal, yVal;
var xIsVal = int.TryParse(xResultString, out xVal);
var yIsVal = int.TryParse(yResultString, out yVal);
if (xIsVal && yIsVal)
return xVal.CompareTo(yVal);
if (!xIsVal && !yIsVal)
return x.CompareTo(y);
if (xIsVal)
return -1;
return 1;
}
}
Edit with what the final output should be:
List =
"Excl 1
Excl 2
Excl 3
Excl 4
Excl 10
Incl 1
Incl 2
Incl 3
Incl 4
Incl 10
Incl 11
Incl 12
Follow Up
Other
Withdrew Col
"
You are almost there. If non-digit part of the values are not same then you need to compare only non-digit parts. Just put these lines in Compare method at the beginning.
var xstring = Regex.Match(x, #".+?(?=\d+)").Value; //extract non-digit part
var ystring = Regex.Match(y, #".+?(?=\d+)").Value; //extract non-digit part
if (!string.IsNullOrEmpty(xstring) && !string.IsNullOrEmpty(ystring))
{
var comp = xstring.CompareTo(ystring);
if (comp != 0)
{
return comp;
}
}
Below is an implementation for your IComparer, which should get you what you want
class MyNumberComparer : IComparer<string> {
public static Tuple<string, int?> SplitString(string s) {
var x = s.LastIndexOf(' ');
return x == -1 || x == s.Length - 1 || !int.TryParse(s.Substring(x + 1), out int n) ?
Tuple.Create(s, (int?)null) : Tuple.Create(s.Substring(0, x), (int?)n);
}
public int Compare(string str1, string str2) {
if (string.Compare(str1, str2) == 0)
return 0;
var str1Items = SplitString(str1);
var str2Items = SplitString(str2);
var prefixCompare = string.Compare(str1Items.Item1, str2Items.Item1);
if (str1Items.Item2 == null && str1Items.Item2 == null)
return prefixCompare;
if (str1Items.Item2 == null)
return 1;
if (str2Items.Item2 == null)
return -1;
return prefixCompare == 0 ? (int)(str1Items.Item2 - str2Items.Item2) : prefixCompare;
}
}
I have based the solution on what you have mentioned in your question (space separated number), but you can change the implementation of SplitString, to reflect breaking the incoming string into a string and a number or null.
If you want the string comparison to happen in a special way (like case insensitive way), then you can update the string.Compare calls in the MyNumberComparer.Compare code to reflect that.
Here's a somewhat simple solution based on regex groups. Tested it, it should give you the results you're looking for. Definitely not pretty though:
class Comparer : IComparer<string> {
static Regex Matcher = new Regex(#"([^\d]+)(\d+)");
public int Compare(string x, string y) {
var xMatch = Matcher.Match(x);
var yMatch = Matcher.Match(y);
if (xMatch.Success != yMatch.Success)
return xMatch.Success ? -1 : 1;
if (!xMatch.Success)
return string.Compare(x, y);
if (xMatch.Groups[1].Value != yMatch.Groups[1].Value)
return string.Compare(xMatch.Groups[1].Value, yMatch.Groups[1].Value);
return (int.Parse(xMatch.Groups[2].Value) - int.Parse(yMatch.Groups[2].Value)) < 0 ? -1 : 1;
}
}
God help you if you have multiple numbers throughout a string lol. In that case, you'll have to alter the regex pattern to look for only digits at the end of the string. Sorry, I got lazy on it.
I have this string for example:
2X+4+(2+2X+4X) +4
The position of the parenthesis can vary. I want to find out how can I extract the part without the parenthesis. For example I want 2X+4+4. Any Suggestions?
I am using C#.
Try simple string Index and Substring operations as follows:
string s = "2X+4+(2+2X+4X)+4";
int beginIndex = s.IndexOf("(");
int endIndex = s.IndexOf(")");
string firstPart = s.Substring(0,beginIndex-1);
string secondPart = s.Substring(endIndex+1,s.Length-endIndex-1);
var result = firstPart + secondPart;
Explanation:
Get the first index of (
Get the first index of )
Create two sub-string, first one is 1 index before beginIndex to remove the mathematical symbol like +
Second one is post endIndex, till string length
Concatenate the two string top get the final result
Try Regex approach:
var str = "(1x+2)-2X+4+(2+2X+4X)+4+(3X+3)";
var regex = new Regex(#"\(\S+?\)\W?");//matches '(1x+2)-', '(2+2X+4X)+', '(3X+3)'
var result = regex.Replace(str, "");//replaces parts above by blank strings: '2X+4+4+'
result = new Regex(#"\W$").Replace(result, "");//replaces last operation '2X+4+4+', if needed
//2X+4+4 ^
Try this one:
var str = "(7X+2)+2X+4+(2+2X+(3X+3)+4X)+4+(3X+3)";
var result =
str
.Aggregate(
new { Result = "", depth = 0 },
(a, x) =>
new
{
Result = a.depth == 0 && x != '(' ? a.Result + x : a.Result,
depth = a.depth + (x == '(' ? 1 : (x == ')' ? -1 : 0))
})
.Result
.Trim('+')
.Replace("++", "+");
//result == "2X+4+4"
This handles nested, preceding, and trailing parenthesis.
Basically, the title says what I'd like to do.
I have a string such as the following.
1 0 1 0 1
0 0 0 0 0
1 0 0 0 1
0 0 0 0 0
1 0 1 0 1
I'd like to convert this into a 2-dimensional boolean array (obviously, 0 -> false and 1 -> true). My current approach is removing non-linebreak-whitespace, then iterating through the lines of the string.
This leaves me with transforming a string such as 10101 into a boolean array of true, false, true, false, true. Now, I'm hoping that there are pre-existing methods to do this conversion - using Java, I'm pretty sure it could be done using the streams API, but unfortunately, I'm not that adept with C# yet.
Hence, my question: Are there existing methods to do this conversion in a compact way? Or do I have to manually iterate over the string and do a == 0/==1 comparison for each character?
Single string
If you have a a string like "10101", you can use LINQ to convert it to a bit array.
string input = "10101";
bool[] flags = input.Select( c => c == '1' ).ToArray();
Array of strings
LINQ doesn't work so well with two-dimensional arrays, but it works fine with jagged arrays. If a bool[][] will do, this solution should work:
string[] input = { "10101","01010" };
bool[][] flags = input.Select
(
s => s.Select
(
c => c == '1'
)
.ToArray()
)
.ToArray();
Here's a relatively ugly "one-liner":
string input = "1 0 1 0 1\n0 0 0 0 0\n1 0 0 0 1\n0 0 0 0 0\n1 0 1 0 1";
bool[][] flags = input.Split(new[] { "\n" }, StringSplitOptions.None) // Split into lines, e.g. [ "1 0 1 0 1", "0 0 0 0 0" ]
.Select(line => line.Split(' ') // Split each line into array of "1"s or "0"s
.Select(num => num == "1").ToArray()) // Project each line into boolean array
.ToArray(); // Get array of arrays
But here is an (arguably more readable) version that probably comes closer to what I suspect is your actual use case, e.g. reading a line from the user one at a time.
string input = "1 0 1 0 1\n0 0 0 0 0\n1 0 0 0 1\n0 0 0 0 0\n1 0 1 0 1";
List<bool[]> boolArrays = new List<bool[]>();
string[] lines = input.Split(new[] { "\n" }, StringSplitOptions.None);
foreach (string line in lines) // Could be a different loop that gets lines from the user, e.g. `do { var s = Console.ReadLine(); doMore(); } while (s != null);`
{
string[] charsAsStrings = line.Split(' '); // TODO: Improve variable names
bool[] arrayRow = charsAsStrings.Select(numString => numString == "1").ToArray(); // Parsing to int would be pointless
boolArrays.Add(arrayRow);
}
bool[][] flags = list.ToArray();
As noted in the comments, you typically want to use Environment.NewLine rather than "\n". Note that we can't split by a string without providing an entire array -- though that can be solved with extension methods.
Similar answer #Sinjai's. This works though by utilizing the fact that a string can be .Selected as a char[].
var boolVals = yourString.Replace(" ","").Replace("\r","")
.Split('\n').Select(x => x.Select(y => y == '1').ToArray()).ToArray();
This works with a scenario where you might or might not have \r\n and spaces between every 1 or 0. If you had a string that looked like this:
string yourString = "10101\n10001";
then the code would be even shorter:
var boolVals = yourString.Split('\n').Select(x => x.Select(y => y == '1').ToArray()).ToArray();
Try using this function:
static List<bool> AsArray(string boolString)
{
List<bool> toArray = new List<bool>();
foreach (char boolChar in boolString)
{
if (boolChar == '0')
{
toArray.Add(false);
}
else if (boolChar == '1')
{
toArray.Add(true);
}
else
{
throw new Exception("String can only be made of 1 and 0.");
}
}
return toArray;
}
The following does exactly what you want to a 5x5 bool array as an example.
code
string str = "1 0 1 0 1 " +
"0 0 0 0 1 " +
"0 1 1 1 0 " +
"1 0 1 0 1 " +
"1 1 1 1 1";
string[] stringArray = str.Split(' '); // split strings into string array using "space" as split
bool[,] boolArray = new bool[5, 5];
int counter = 0; // counter for iterating through stringArray
int counterMax = stringArray.Length - 1; // set max for counter to stop index out of range
for (int j = 0; j < 5; j++) // Y axis
{
for (int i = 0; i < 5; i++) // X axis
{
if (stringArray[counter] == "0")
{
boolArray[j, i] = false;
}
else
{
boolArray[j, i] = true;
}
if (counter < counterMax)
{
counter++;
}
else
{
break;
}
}
}
I have a string
string value = "123456789";
now I need to re-arrange the string in the following way:
123456789
1 right
12 left
312 right
3124 left
53124 right
...
975312468 result
Is there a fancy linq one liner solution to solve this?
My current (working but not so good looking) solution:
string items = "abcdefgij";
string result = string.Empty;
for (int i = 0; i < items.Length; i++)
{
if (i % 2 != 0)
{
result = result + items[i];
}
else
{
result = items[i] + result;
}
}
string value = "123456789";
bool b = true;
string result = value.Aggregate(string.Empty, (s, c) =>
{
b = !b;
return b ? (s + c) : (c + s);
});
I actually don't like local variables inside LINQ statements, but in this case b helps alternating the direction. (#klappvisor showed how to live without b).
You can use length of the res as variable to decide from which side to append
items.Aggregate(string.Empty, (res, c) => res.Length % 2 == 0 ? c + res : res + c);
Alternative solution would be zipping with range
items.Zip(Enumerable.Range(0, items.Length), (c, i) => new {C = c, I = i})
.Aggregate(string.Empty, (res, x) => x.I % 2 == 0 ? x.C + res : res + x.C)
EDIT: don't really needed ToCharArray...
Resulting string is chars in evens positions concatenated to chars in odds positions in reverse order:
string value = "123456789";
var evens = value.Where((c, i) => i % 2 == 1);
var odds = value.Where((c, i) => i % 2 == 0).Reverse();
var chars = odds.Concat(evens).ToArray();
var result = new string(chars);