I am working on a routine in C#
I have a list of alphanumeric sheet numbers that I would like to retrieve the numbers before the decimal to use in my routine.
FP10.01-->10
M1.01-->1
PP8.01-->8
If possible, how can something like this be achieved as either a string or integer?
You could use a regex:
Regex r = new Regex("([0-9]+)[.]");
string s = "FP10.01";
var result = Convert.ToInt32(r.Match(s).Groups[1].ToString()); //10
string input = "FP10.01";
string[] _input = input.Split('.');
string num = find(_input[0]);
public string find(string input)
{
char[] _input = input.ToArray();
int number;
string result = null;
foreach (var item in _input)
{
if (int.TryParse(item.ToString(), out number) == true)
{
result = result + number;
}
}
return result;
}
To accumulate the resulting elements into a list, you can do something like:
List<string> myList = new List<string>(){ "FP10.01","M1.01", "PP8.01"};
List<int> resultSet =
myList.Select(e =>
Regex.Replace(e.Substring(0, e.IndexOf('.')), #"[^\d]", string.Empty))
.Select(int.Parse)
.ToList();
This will take each element in myList and in turn, take a substring of each element from index 0 until before the . and then replace all the non-numeric data with string.Empty and then finally parse the string element into an int and store it into a list.
another variant would be:
List<int> resultSet =
myList.Select(e => e.Substring(0, e.IndexOf('.')))
.Select(e => string.Join(string.Empty, e.Where(char.IsDigit)))
.Select(int.Parse)
.ToList();
or if you want the elements to be strings then you could do:
List<string> resultSet =
myList.Select(e => e.Substring(0, e.IndexOf('.')))
.Select(e => string.Join(string.Empty, e.Where(char.IsDigit)))
.ToList();
To retrieve a single element of type string then you can create a helper function as such:
public static string GetValueBeforeDot(string input){
return input.Substring(0, input.IndexOf('.'))
.Where(char.IsDigit)
.Aggregate(string.Empty, (e, a) => e + a);
}
To retrieve a single element of type int then the helper function should be:
public static int GetValueBeforeDot(string input){
return int.Parse(input.Substring(0, input.IndexOf('.'))
.Where(char.IsDigit)
.Aggregate(string.Empty, (e, a) => e + a));
}
This approach removes alphabet characters by replacing them with an empty string. Splitting on the '.' character will leave you with a two element array consisting of numbers at index 0 and after decimal values at index 1.
string input = "FP10.01";
var result = Regex.Replace(input, #"([A-Za-z]+)", string.Empty).Split('.');
var beforeDecimalNumbers = result[0]; // 10
var afterDecimalNumbers = result[1]; // 01
Related
I have to convert an hexadecimal string starting with 0x or an hexadecimal string converted with BitConverter to a byte array. For this I use this function which works very well:
public static byte[] ConvertToByteArray(this string s)
{
if (s.StartsWith("0x"))
{
var ret = new byte[(s.Length - 2) / 2];
for (int i = 2; i < s.Length; i += 2)
{
ret[(i - 2) / 2] = Convert.ToByte(string.Concat(s[i], s[i + 1]), 16);
}
return ret;
}
else
return s.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
}
example input, coming from a sort of a network device (think of it as a message logged with wireshark ) :
byte[] data1 = "0x020206000000022800A601585E40".ConvertToByteArray();
byte[] data2 = "02-02-06-00-00-00-02-28-00-A6-01-58-5E-40".ConvertToByteArray();
CollectionAssert.AreEqual(data1, data2);
Now I would like to understand how to write the first possibility (starts with 0x) in LINQ to get rid of this 1990ish for loop.
Is there a way to Select two characters at the same time, or is there a more elegant way then mine?
This is the linq equivalent of your loop regardless of any other consideration:
if (s.StartsWith("0x"))
{
return
s.Skip(2)
.Select((x,i) => new {index = i, value = x})
.GroupBy(pair => pair.index / 2)
.Select(grp => string.Join("", grp.Select(x=>x.value)))
.Select(x => Convert.ToByte(x,16))
.ToArray();
}
But this seems to be a solution to your consideration of not having a 90ish code:
public static byte[] ConvertToByteArray(this string s)
{
string tmp = s.Replace("0x","").Replace("-","");
tmp = Regex.Replace(tmp, ".{2}", "$0-");
return tmp.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
}
Well i think first you can have a look at this
Using #jdweng sample input.
string input = "0x0123456789ABCDE".Replace("0x", string.Empty);
long intValue = long.Parse(s, System.Globalization.NumberStyles.HexNumber);
Now if you have long you can convert it to byte[] easly.
byte[] array = BitConverter.GetBytes(intValue);
I know it it's not LINQ solution to your problem but it's clean and simple.
Update:
After reading OP's comment, I see he wants to be able to consume hex strings of an arbitrary length. I'd be tempted to use an iterator function to return your hex pairs, to match the result of your split. Then you can feed either enumerable through the same conversion, like so:
public byte[] ConvertToByteArray(string s)
{
IEnumerable<string> query = Enumerable.Empty<string>();
if (s.StartsWith("0x"))
{
query = IterateHexPairs(s.Substring(2));
}
else
{
query = s.Split('-');
}
return query.Select(b => Convert.ToByte(b, 16)).ToArray();
IEnumerable<string> IterateHexPairs(string hexLiteral)
{
char? previousNibble = null;
foreach (var nibble in hexLiteral)
{
if (previousNibble != null)
{
yield return new string(new char[] { previousNibble.Value, nibble });
previousNibble = null;
}
else
{
previousNibble = nibble;
}
}
}
}
This avoids having to duplicate your conversion logic, as they both get fed from an IEnumerable. The only difference is the source of the IEnumerable. Change the code that gives you the enumerable as you see fit. I thought an Iterator function would be more maintainable, but you could bodge a Linq query to achieve the same result, like this:
public byte[] ConvertToByteArray(string s)
{
IEnumerable<string> query = Enumerable.Empty<string>();
if (s.StartsWith("0x"))
{
// omit the 0x
query = s.Skip(2)
// get the char and index, so we can pair them up
.Select((c, i) => new { Char = c, Index = i })
// group them into pairs
.GroupBy(o => o.Index / 2)
// select them as new strings, so they can be converted
.Select(g => new string(g.Select(o => o.Char).ToArray()));
}
else
{
query = s.Split('-');
}
return query.Select(b => Convert.ToByte(b, 16)).ToArray();
}
I am trying to figure out a regex to use to split a string into 2 character substring.
Let's say we have the following string:
string str = "Idno1";
string pattern = #"\w{2}";
Using the pattern above will get me "Id" and "no", but it will skip the "1" since it doesn't match the pattern. I would like the following results:
string str = "Idno1"; // ==> "Id" "no" "1 "
string str2 = "Id n o 2"; // ==> "Id", " n", " o", " 2"
Linq can make easy the code. Fiddle version works
The idea: I have a chunkSize = 2 as your requirement, then, Take the string at the index (2,4,6,8,...) to get the chunk of chars and Join them to string.
public static IEnumerable<string> ProperFormat(string s)
{
var chunkSize = 2;
return s.Where((x,i) => i % chunkSize == 0)
.Select((x,i) => s.Skip(i * chunkSize).Take(chunkSize))
.Select(x=> string.Join("", x));
}
With the input, I have the output
Idno1 -->
Id
no
1
Id n o 2 -->
Id
n
o
2
Linq is really better in this case. You can use this method - it will allow to split string in chunks of arbitrary size:
public static IEnumerable<string> SplitInChunks(string s, int size = 2)
{
return s.Select((c, i) => new {c, id = i / size})
.GroupBy(x => x.id, x => x.c)
.Select(g => new string(g.ToArray()));
}
But if you are bound to regex, use this code:
public static IEnumerable<string> SplitInChunksWithRegex(string s, int size = 2)
{
var regex = new Regex($".{{1,{size}}}");
return regex.Matches(s).Cast<Match>().Select(m => m.Value);
}
If I had the following code:
string x = "123";
string y = "abc";
if (stringVar.Contains(x))
{
return x;
}
else if (stringVar.Contains(y))
{
return y;
}
where
string stringVar = "123abc";
it would
return x;
However
stringVar = "abc123";
would also
return x;
Is there a way where instead of following the pre-defined order of the if-else statement, I could have the return result be based upon the order of stringVar instead?
My desired result is if:
stringVar = "123abc";
...
return x;
and if:
stringVar = "abc123";
...
return y;
EDIT:
This example is a very simplified example of my current problem, String.StartsWith() would not always work as the text I am looking for is not always at the start of stringVar. Additionally, in my actual problem there are no distinct separators between words (i.e. no spaces) either making it difficult to split stringVar.
First i would suggest to store the items in a collection, for example:
string[] words = {"123","abc"};
Now you can use string.IndexOf and LINQ:
return words
.Where(stringVar.Contains)
.OrderBy(stringVar.IndexOf)
.FirstOrDefault();
try string.IndexOf(). it returns the first index of a substring else, -1
then you can return based on which number is lower.
"abc123".indexOf("abc") = 0
"abc123".indexOf("123") = 3
Put search strings in an array, compute IndexOf, and pick the earliest match:
var searchStrings = new[] {"abc", "123"};
var res = searchStrings.Select(s => new { // Pair up a string with its index in stringVar
Value = s
, Index = stringVar.IndexOf(s)
})
.Where(p => p.Index >= 0) // Remove strings that did not produce a match
.OrderBy(p => p.Index) // Order by the match position
.FirstOrDefault(p => p.Value); // Pick the earliest one
Here's a non-Linq way to do it.
var subStrings = new[] {"abc", "123"};
string result = null;
int position = stringVar.Length;
foreach(var sub in subStrings)
{
var currPos = stringVar.IndexOf(sub);
if(currPos > -1 && currPos < position)
{
position = currPos;
result = sub;
}
}
return result;
I have a string
string test1 = "255\r\n\r\n0\r\n\r\n-1\r\n\r\n255\r\n\r\n1\r";
I want to find all the 1's in my string but not the -1's. So in my string there is only one 1. I use string.Contain("1") but this will find two 1's. So how do i do this?
You can use regular expression:
string test1 = "255\r\n\r\n0\r\n\r\n-1\r\n\r\n255\r\n\r\n1\r";
// if at least one "1", but not "-1"
if (Regex.IsMatch(test1, "(?<!-)1")) {
...
}
the pattern is exactly 1 which is not preceed by -. To find all the 1s:
var matches = Regex
.Matches(test1, "(?<!-)1")
.OfType<Match>()
.ToArray(); // if you want an array
Try this simple solution:
Note : You can convert this to extension Method Easily.
static List<int> FindIndexSpecial(string search, char find, char ignoreIfPreceededBy)
{
// Map each Character with its Index in the String
var characterIndexMapping = search.Select((x, y) => new { character = x, index = y }).ToList();
// Check the Indexes of the excluded Character
var excludeIndexes = characterIndexMapping.Where(x => x.character == ignoreIfPreceededBy).Select(x => x.index).ToList();
// Return only Indexes who match the 'find' and are not preceeded by the excluded character
return (from t in characterIndexMapping
where t.character == find && !excludeIndexes.Contains(t.index - 1)
select t.index).ToList();
}
Usage :
static void Main(string[] args)
{
string test1 = "255\r\n\r\n0\r\n\r\n-1\r\n\r\n255\r\n\r\n1\r";
var matches = FindIndexSpecial(test1, '1', '-');
foreach (int index in matches)
{
Console.WriteLine(index);
}
Console.ReadKey();
}
You could use String.Split and Enumerable.Contains or Enumerable.Where:
string[] lines = test1.Split(new[] {Environment.NewLine, "\r"}, StringSplitOptions.RemoveEmptyEntries);
bool contains1 = lines.Contains("1");
string[] allOnes = lines.Where(l => l == "1").ToArray();
String.Contains searches for sub-strings in a given string instance. Enumerable.Contains looks if there's at least one string in the string[] which equals it.
If we have a list of strings, then how we can find the list of strings that have the maximum number of repeated symbol by using LINQ.
List <string> mylist=new List <string>();
mylist.Add("%1");
mylist.Add("%136%250%3"); //s0
mylist.Add("%1%5%20%1%10%50%8%3"); // s1
mylist.Add("%4%255%20%1%14%50%8%4"); // s2
string symbol="%";
List <string> List_has_MAX_num_of_symbol= mylist.OrderByDescending(s => s.Length ==max_num_of(symbol)).ToList();
//the result should be a list of s1 + s2 since they have **8** repeated '%'
I tried
var longest = mylist.Where(s => s.Length == mylist.Max(m => m.Length)) ;
this gives me only one string not both
Here's a very simple solution, but not exactly efficient. Every element has the Count operation performed twice...
List<string> mylist = new List<string>();
mylist.Add("%1");
mylist.Add("%136%250%3"); //s0
mylist.Add("%1%5%20%1%10%50%8%3"); // s1
mylist.Add("%4%255%20%1%14%50%8%4"); // s2
char symbol = '%';
var maxRepeat = mylist.Max(item => item.Count(c => c == symbol));
var longest = mylist.Where(item => item.Count(c => c == symbol) == maxRepeat);
It will return 2 strings:
"%1%5%20%1%10%50%8%3"
"%4%255%20%1%14%50%8%4"
Here is an implementation that depends upon SortedDictionary<,> to get what you're after.
var mylist = new List<string> {"%1", "%136%250%3", "%1%5%20%1%10%50%8%3", "%4%255%20%1%14%50%8%4"};
var mappedValues = new SortedDictionary<int, IList<string>>();
mylist.ForEach(str =>
{
var count = str.Count(c => c == '%');
if (mappedValues.ContainsKey(count))
{
mappedValues[count].Add(str);
}
else
{
mappedValues[count] = new List<string> { str };
}
});
// output to validate output
foreach (var str in mappedValues.Last().Value)
{
Console.WriteLine(str);
}
Here's one using LINQ that gets the result you're after.
var result = (from str in mylist
group str by str.Count(c => c == '%')
into g
let max = (from gKey in g select g.Key).Max()
select new
{
Count = max,
List = (from str2 in g select str2)
}).LastOrDefault();
OK, here's my answer:
char symbol = '%';
var recs = mylist.Select(s => new { Str = s, Count = s.Count(c => c == symbol) });
var maxCount = recs.Max(x => x.Count);
var longest = recs.Where(x => x.Count == maxCount).Select(x => x.Str).ToList();
It is complicated because it has three lines (the char symbol = '%'; line excluded), but it counts each string only once. EZI's answer has only two lines, but it is complicated because it counts each string twice. If you really want a one-liner, here it is:
var longest = mylist.Where(x => x.Count(c => c == symbol) == mylist.Max(y => y.Count(c => c == symbol))).ToList();
but it counts each string many times. You can choose whatever complexity you want.
We can't assume that the % is always going to be the most repeated character in your list. First, we have to determine what character appears the most in an individual string for each string.
Once we have the character and it maximum occurrence, we can apply Linq to the List<string> and grab the strings that contain the character equal to its max occurrence.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List <string> mylist=new List <string>();
mylist.Add("%1");
mylist.Add("%136%250%3");
mylist.Add("%1%5%20%1%10%50%8%3");
mylist.Add("%4%255%20%1%14%50%8%4");
// Determine what character appears most in a single string in the list
char maxCharacter = ' ';
int maxCount = 0;
foreach (string item in mylist)
{
// Get the max occurrence of each character
int max = item.Max(m => item.Count(c => c == m));
if (max > maxCount)
{
maxCount = max;
// Store the character whose occurrence equals the max
maxCharacter = item.Select(c => c).Where(c => item.Count(i => i == c) == max).First();
}
}
// Print the strings containing the max character
mylist.Where(item => item.Count(c => c == maxCharacter) == maxCount)
.ToList().ForEach(Console.WriteLine);
}
}
Results:
%1%5%20%1%10%50%8%3
%4%255%20%1%14%50%8%4
Fiddle Demo
var newList = myList.maxBy(x=>x.Count(y=>y.Equals('%'))).ToList();
This should work. Please correct syntax if wrong anywhere and update here too if it works for you.