How can I split an eight character string into four variables? - c#

I hava a string like this:
var id = "01020304";
Is there a simple way I can split this into four variables called pr, pa, fo, it Each variable needs to have the two characters of the string. Looking for an elegant solution if it exists.

You can use Substring:
pr = id.Substring(0, 2);
pa = id.Substring(2, 2);
fo = id.Substring(4, 2);
it = id.Substring(6, 2);

Since you have four distinct variables, you can't get much more elegant than using Substring:
var pr = id.Substring(0, 2);
var pa = id.Substring(2, 2);
var fo = id.Substring(4, 2);
var it = id.Substring(6);
Were you looking for an array of four 2-character substrings, you could get fancier:
var parts = new string[4];
for (int i = 0 ; i != parts.Length ; i ++) {
parts[i] = id.Substring(2*i, 2);
}
EDIT: The same can be done with a LINQ expression:
var parts = Enumerable
.Range(0, id.Length/2)
.Select(i => x.Substring(2*i, 2))
.ToArray();

Try regular expression.
var id = "01020304";
string pat = "(?:(\\d{2}))";
var result = Regex.Split(id, pat).Where(p=>p!=string.Empty);
foreach (var t in result)
{
Console.WriteLine(t);
}

If you are always going to have an input of 8 characters and always require 4 variables, you can simply split the string with the Substring(...) method:
var id = "01020304";
string pr = id.Substring(0, 2);
string pa = id.Substring(2, 2);
string fo = id.Substring(4, 2);
string it = id.Substring(6, 2);
Otherwise, you can employ a method of running through a for loop and splitting off two characters at a time.

Here is a solution with a loop, which would support strings of this format, any length:
string id = "01020304";
int length = id.Length;
int[] holder = new int[length/2];
for (int i = 0; i < length/2; i++) {
holder[i] = id.Substring(i*2, 2);
}

Here is a version using Linq.
Interestingly enough, this is not so easy to achieve with just the built-in operators. The version below just uses the Linq extension methods that come with the .NET framework:
var result = "01020304".ToCharArray().
Select((c,i) => new { idx = i % 2 == 1 ? i - 1 : i, ch = c }).
GroupBy(e => e.idx,
(k,g) => new String(g.Select(e => e.ch).ToArray()));
If you use the morelinq Extensions the query can be simplified to
var result = "01020304".ToCharArray().
Batch(2).Select(ca => new String(ca.ToArray()));

Related

Is there a better way (fastest) to get the longest common folder path?

I have a folder list,
want to get the longest common substring as output.
Here's my code, seems not very good,
How to improve these?
These files all from My RecursiveSearch function, It may be D:\123, E:\456, F:\a\e\eff, I save to xml.
Later read back from xml, want to get previous decision.
Files could be ten-thousands.
I do care about the speed.
var li = new List<string>()
{
#"C:\Users\jared\Desktop\fld1\eumaps\4.jfif",
#"C:\Users\jared\Desktop\fld2\eumaps - (2)\4.jfif",
#"C:\Users\jared\Desktop\fld4\ade\4.jfif",
#"C:\Users\jared\Desktop\fld4\abc\S.png",
#"C:\Users\jared\Desktop\fld1\file\Snipaste_2021-07-07_03-03-45.png",
};
//var shortest1 = li.OrderBy(name => name.Length).FirstOrDefault();
string shortest = li.Aggregate((a1, a2) => a1.Length < a2.Length ? a1 : a2);
string longest = li.Aggregate((a1, a2) => a1.Length > a2.Length ? a1 : a2);
string common = string.Concat(shortest.TakeWhile((c, i) => c == longest[i]));
// C:\Users\jared\Desktop\fld NOT MY WANT CAUSE THERE IS NO SUCH FOLDER.
if (!Directory.Exists(common))
{
common = common.Replace(common.Split("\\").Last(), "");
}
Your approach would fail as you are just comparing the shortest and the longest, if there is any other path with different length and have different path in middle.
As per my understanding you want the output to be till the matched folder (not the substring of the folder or filename) i.e. C:\Users\jared\Desktop\
Here is my solution which runs in O(Nk) solution, Where N is number of paths and k is the shortest available length.
Working Sample Code here.
This can also be done using the Trie Data structure in more optimized way (O(N + k)) but it would take extra space to build the Trie
var folders = new List<string>()
{
#"C:\Users\jared\Desktop\fld1\eumaps\4.jfif",
#"C:\Users\jared\Desktop\fld2\eumaps - (2)\4.jfif",
#"C:\Users\jared\Desktop\fld4\ade\4.jfif",
#"C:\Users\jared\Desktop\fld4\abc\S.png",
#"C:\Users\jared\Desktop\fld1\file\Snipaste_2021-07-07_03-03-45.png",
};
var minPathLength = folders.Min(x => x.Length);
var maxCommonPath = new StringBuilder();
var currentCommonPath = new StringBuilder();
for (int i = 0; i < minPathLength; i++)
{
var boolAllSame = true;
var c = folders[0][i];
boolAllSame = folders.All(x => x[i] == c);
if (boolAllSame)
{
currentCommonPath.Append(c);
if (c == '\\')
{
maxCommonPath.Append(currentCommonPath.ToString());
currentCommonPath = new StringBuilder();
}
}
else
break;
}
var result = maxCommonPath.ToString();
Console.WriteLine(result);
Here is a succinct, but not particularly performant implementation that uses Linq. It also has a dependency on the MoreLinq package, for the Transpose operator:
var paths = new List<string>()
{
#"C:\Users\jared\Desktop\fld1\eumaps\4.jfif",
#"C:\Users\jared\Desktop\fld2\eumaps - (2)\4.jfif",
#"C:\Users\jared\Desktop\fld4\ade\4.jfif",
#"C:\Users\jared\Desktop\fld4\abc\S.png",
#"C:\Users\jared\Desktop\fld1\file\Snipaste_2021-07-07_03-03-45.png",
};
string[] longestCommonPathComponents = paths
.Select(path => path.Split(Path.DirectorySeparatorChar))
.Transpose()
.Select(parts => parts.Distinct(StringComparer.OrdinalIgnoreCase))
.TakeWhile(distinct => distinct.Count() == 1)
.Select(distinct => distinct.First())
.ToArray();
string longestCommonPath = Path.Combine(longestCommonPathComponents);
Console.WriteLine($"Longest common path: {longestCommonPath}");
Output:
Longest common path: C:/Users/jared/Desktop
Try it on fiddle.
The signature of the Transpose operator:
// Transposes a sequence of rows into a sequence of columns.
public static IEnumerable<IEnumerable<T>> Transpose<T>(
this IEnumerable<IEnumerable<T>> source);
var li = new List<string>()
{
#"C:\Users\jared\Desktop\fld1\eumaps\4.jfif",
#"C:\Users\jared\Desktop\fld2\eumaps - (2)\4.jfif",
#"C:\Users\jared\Desktop\fld4\ade\4.jfif",
#"C:\Users\jared\Desktop\fld4\abc\S.png",
#"C:\Users\jared\Desktop\fld1\file\Snipaste_2021-07-07_03-03-45.png",
};
string first = li.First();
int n = 0;
while (li.All(x => x.Length > n && x[n] == first[n]))
n++;
string longestCommon = first.Substring(0, n); // C:\Users\jared\Desktop\fld
You say C:\Users\jared\Desktop\fld is not what you want, but what do you want? The most nested valid folder? Just pull to the last slash, or use Path methods to get the bit you want.
suggestion:
use this code for your image paths
var li = new List<string>()
{
Application.StartupPath + "/Images/1.png",
Application.StartupPath + "/Images/2.png",
Application.StartupPath + "/Images/3.png",
}

Looking for next match (integer) in list of strings

I have a problem finding the next integer match in a list of strings, there are some other aspects to consider:
single string contains non relevant trailing and leading chars
numbers are formatted "D6" example 000042
there are gaps in the numbers
the list is not sorted, but it could be if there is a fast way to ignore the leading chars
Example:
abc-000001.file
aaac-000002.file
ab-002010.file
abbc-00003.file
abbbc-00004.file
abcd-00008.file
abc-000010.file
x-902010.file
The user input is 7 => next matching string would be abcd-000008.file
My attempt is :
int userInput = 0;
int counter = 0;
string found = String.Empty;
bool run = true;
while (run)
{
for (int i = 0; i < strList.Count; i++)
{
if(strList[i].Contains((userInput + counter).ToString("D6")))
{
found = strList[i];
run = false;
break;
}
}
counter++;
}
It's bad because it's slow and it can turn into a infinite loop. But I really don't know how to do this (fast).
You can parse numbers from strings with Regex and created a sorted collection which you can search with Where clause:
var strings = new[] { "abc-000001.file", "x-000004.file"};
var regP = "\\d{6}"; // simplest option in my example, maybe something more complicated will be needed
var reg = new Regex(regP);
var collection = strings
.Select(s =>
{
var num = reg.Match(s).Captures.First().Value;
return new { num = int.Parse(num), str = s};
})
.OrderBy(arg => arg.num)
.ToList();
var userInput = 2;
var res = collection
.Where(arg => arg.num >= userInput)
.FirstOrDefault()?.str; // x-000004.file
P.S.
How 9002010, 0000010, 0002010 should be treated? Cause they have 7 characters. Is it [9002010, 10, 2010] or [900201, 1, 201]?
If you don't want regex, you can do something like that:
List<string> strings = new List<string>
{
"abc-000001.file",
"aaac-000002.file",
"ab-0002010.file",
"abbc-000003.file",
"abbbc-000004.file",
"abcd-000008.file"
};
int input = 7;
var converted = strings.Select(s => new { value = Int32.Parse(s.Split('-', '.')[1]), str = s })
.OrderBy(c => c.value);
string result = converted.FirstOrDefault(v => v.value >= input)?.str;
Console.WriteLine(result);

Extract table name from schema and table name

I'm trying to get the table name from a string that is in the format:
[schemaname].[tablename]
I think this can be done with split but not sure how to handle the trailing ] character.
A simple approach is using String.Split and String.Trim in this little LINQ query:
string input = "[schemaname].[tablename]";
string[] schemaAndTable = input.Split('.')
.Select(t => t.Trim('[', ']'))
.ToArray();
string schema = schemaAndTable[0];
string table = schemaAndTable[1];
Another one using IndexOf and Substring:
int pointIndex = input.IndexOf('.');
if(pointIndex >= 0)
{
string schema = input.Substring(0, pointIndex).Trim('[', ']');
string table = input.Substring(pointIndex + 1).Trim('[', ']');
}
//find the seperator
var pos = str.IndexOf('].[');
if (pos == -1)
return null; //sorry, can't be found.
//copy everything from the find position, but ignore ].[
// and also ignore the last ]
var tableName = str.Substr(pos + 3, str.Length - pos - 4);
Just to be the different here is another version with regex;
var result = Regex.Match(s, #"(?<=\.\[)\w+").Value;
Split by 3 characters. i.e [.] with option RemoveEmptyEntries that is pretty self explanatory.
var result = input.Split(new [] {'[','.',']'}, StringSplitOptions.RemoveEmptyEntries);
Try this:
var tableAndSchema = "[schemaname].[tablename]";
var tableName = tableAndSchema
.Split('.')[1]
.TrimStart('[')
.TrimEnd(']');
Split will split the string on the . character and turn it into an array of two strings:
[0] = "[schemaname]"
[1] = "[tablename]"
The second (index 1) element is the one you want. TrimStart and TrimEnd will remove the starting and ending brackets.
Another way to do this is with Regular Expressions:
var tableAndSchema = "[schemaname].[tablename]";
var regex = new Regex(#"\[.*\].\[(.*)\]");
var tableName = regex.Match(tableAndSchema).Groups[1];
The regex pattern \[.*\].\[(.*)\] creates a capture group for the characters within the second pair of brackets and lets you easily pull them out.
var res = input.Split('.')[1].Trim('[', ']');
Another LINQ solution:
var tableName = String.Join("", input.SkipWhile(c => c != '.').Skip(1)
.Where(c => Char.IsLetter(c)));

parse querystring in asp.net with mutiple values seperated by colon

I have a strange querystring formation that I need to parse. The format is - key=N:1042,B:10,C:200 . I havent encountered this format in the past, is there an easy way to extract the values of "N" , "B" , and "C" in asp.net?
Thanks!
Just a suggestion, you can also use LINQ to get parsed/split values. Like following.
var val = Request.QueryString.Get("key"); //"N:1042,B:10,C:200"
if (val.IndexOf(",") != -1)
{
var parsedValue = (from m in val.Split(',')
where m.Split(':').Count() == 2
select new { key = m.Split(':')[0], value = m.Split(':')[1] });
}
Output
use the Split Method :
string key = "N:1042,B:10,C:200";
string[] values = key.Split(',');
for (int i = 0; i < values.Length; i++)
{
var nbc = values[i].Split(':')[1];
//Do something with your var..
}
more info here : Split method MSDN
Very simply:
var val = Request.QueryString.Get("key");
var terms = val.Split(',');
foreach (var term in terms)
{
var pair = term.Split(':');
var key = pair[0];
var v = pair[1];
}

Get n strings from Linq

I am trying to get every 5 "NewNumber" int's to insert in to var q. Let's say there are 20 records returned by UniqueNumbers, I would like to get 1-5, 6-10, 11-15, 16-20 and then have Number1 = 1,Number2 = 2,Number3 = 3,Number4 = 4,Number5 = 5 passed to var q the first time, followed by Number1 = 6, Number2 = 7, Number3 = 8, Number4 = 9, Number5 = 10 and so on...
var UniqueNumbers =
from t in Numbers
group t by new { t.Id } into g
select new
{
NewNumber = g.Key.Id,
};
UniqueNumbers.Skip(0).Take(5)
var q = new SolrQueryInList("NewNumber1", "NewNumber2","NewNumber3","NewNumber4","NewNumber5");
If you have a list of items, you can easily separate them into groups of five like this:
int count = 0;
var groupsOfFive =
from t in remaining
group t by count++ / 5 into g
select new { Key=g.Key, Numbers = g };
And then:
foreach (var g in groupsOfFive)
{
var parms = g.Numbers.Select(n => n.ToString()).ToArray();
var q = new SolrQueryInList(parms[0], parms[1], parms[2], parms[3], parms[4]);
}
I think what you want is some variation on that.
Edit
Another way to do it, if for some reason you don't want to do the grouping, would be:
var items = remaining.Select(n => n.ToString()).ToArray();
for (int current = 0; current < remaining.Length; remaining += 5)
{
var q = new SolrQueryInList(
items[current],
items[current+1],
items[current+2],
items[current+3],
items[current+4]);
}
Both of these assume that the number of items is evenly divisible by 5. If it's not, you have to handle the possibility of not enough parameters.
Try something like this:
for (int i = 0; i < UniqueNumbers.Count / 5; i++)
{
// Gets the next 5 numbers
var group = UniqueNumbers.Skip(i * 5).Take(5);
// Convert the numbers to strings
var stringNumbers = group.Select(n => n.ToString()).ToList();
// Pass the numbers into the method
var q = new SolrQueryInList(stringNumbers[0], stringNumbers[1], ...
}
You'll have to figure out how to manage boundary conditions, like if UniqueNumbers.Count is not divisible by 5. You might also be able to modify SolrQueryInList to take a list of numbers so that you don't have to index into the list 5 times for that call.
EDIT:
Jim Mischel pointed out that looping over a Skip operation gets expensive fast. Here's a variant that keeps your place, rather than starting at the beginning of the list every time:
var remaining = UniqueNumbers;
while(remaining.Any())
{
// Gets the next 5 numbers
var group = remaining.Take(5);
// Convert the numbers to strings
var stringNumbers = group.Select(n => n.ToString()).ToList();
// Pass the numbers into the method
var q = new SolrQueryInList(stringNumbers[0], stringNumbers[1], ...
// Update the starting spot
remaining = remaining.Skip(5);
}

Categories

Resources