String manipulation in alternating order - c#

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);

Related

c# ordering strings with different formats

I have Licence plate numbers which I return to UI and I want them ordered in asc order:
So let's say the input is as below:
1/12/13/2
1/12/11/3
1/12/12/2
1/12/12/1
My expected output is:
1/12/11/3
1/12/12/1
1/12/12/2
1/12/13/2
My current code which is working to do this is:
var orderedData = allLicenceNumbers
.OrderBy(x => x.LicenceNumber.Length)
.ThenBy(x => x.LicenceNumber)
.ToList();
However for another input sample as below:
4/032/004/2
4/032/004/9
4/032/004/3/A
4/032/004/3/B
4/032/004/11
I am getting the data returned as:
4/032/004/2
4/032/004/9
4/032/004/11
4/032/004/3/A
4/032/004/3/B
when what I need is:
4/032/004/2
4/032/004/3/A
4/032/004/3/B
4/032/004/9
4/032/004/11
Is there a better way I can order this simply to give correct result in both sample inputs or will I need to write a custom sort?
EDIT
It wont always be the same element on the string.
This could be example input:
2/3/5/1/A
1/4/6/7
1/3/8/9/B
1/3/8/9/A
1/5/6/7
Expected output would be:
1/3/8/9/A
1/3/8/9/B
1/4/6/7
1/5/6/7
2/3/5/1/A
You should split your numbers and compare each part with each other. Compare numbers by value and strings lexicographically.
var licenceNumbers = new[]
{
"4/032/004/2",
"4/032/004/9",
"4/032/004/3",
"4/032/004/3/A",
"4/032/004/3/B",
"4/032/004/11"
};
var ordered = licenceNumbers
.Select(n => n.Split(new[] { '/' }))
.OrderBy(t => t, new LicenceNumberComparer())
.Select(t => String.Join("/", t));
Using the following comparer:
public class LicenceNumberComparer: IComparer<string[]>
{
public int Compare(string[] a, string[] b)
{
var len = Math.Min(a.Length, b.Length);
for(var i = 0; i < len; i++)
{
var aIsNum = int.TryParse(a[i], out int aNum);
var bIsNum = int.TryParse(b[i], out int bNum);
if (aIsNum && bIsNum)
{
if (aNum != bNum)
{
return aNum - bNum;
}
}
else
{
var strCompare = String.Compare(a[i], b[i]);
if (strCompare != 0)
{
return strCompare;
}
}
}
return a.Length - b.Length;
}
}
If we can assume that
Number plate constist of several (one or more) parts separated by '/', e.g. 4, 032, 004, 2
Each part is not longer than some constant value (3 in the code below)
Each part consist of either digits (e.g. 4, 032) or non-digits (e.g. A, B)
We can just PadLeft each number plate's digit part with 0 in order to compare not "3" and "11" (and get "3" > "11") but padded "003" < "011":
var source = new string[] {
"4/032/004/2",
"4/032/004/9",
"4/032/004/3/A",
"4/032/004/3/B",
"4/032/004/11",
};
var ordered = source
.OrderBy(item => string.Concat(item
.Split('/') // for each part
.Select(part => part.All(char.IsDigit) // we either
? part.PadLeft(3, '0') // Pad digit parts e.g. 3 -> 003, 11 -> 011
: part))); // ..or leave it as is
Console.WriteLine(string.Join(Environment.NewLine, ordered));
Outcome:
4/032/004/2
4/032/004/3/A
4/032/004/3/B
4/032/004/9
4/032/004/11
You seem to be wanting to sort on the fourth element of the string (delimited by /) in numeric rather than string mode.. ?
You can make a lambda more involved/multi-statement by putting it like any other method code block, in { }
var orderedData = allLicenceNumbers
.OrderBy(x =>
{
var t = x.Split('/');
if(t.Length<4)
return -1;
else{
int o = -1;
int.TryParse(t[3], out o);
return o;
}
)
.ToList();
If you're after sorting on more elements of the string, you might want to look at some alternative logic, perhaps if the first part of the string will always be in the form N/NNN/NNN/??/?, then do:
var orderedData = allLicenceNumbers
.OrderBy(w => w.Remove(9)) //the first 9 are always in the form N/NNN/NNN
.ThenBy(x => //then there's maybe a number that should be parsed
{
var t = x.Split('/');
if(t.Length<4)
return -1;
else{
int o = -1;
int.TryParse(t[3], out o);
return o;
}
)
.ThenBy(y => y.Substring(y.LastIndexOf('/'))) //then there's maybe A or B..
.ToList();
Ultimately, it seems that more and more outliers will be thrown into the mix, so you're just going to have to keep inventing rules to sort with..
Either that or change your strings to standardize everything (int an NNN/NNN/NNN/NNN/NNA format for example), and then sort as strings..
var orderedData = allLicenceNumbers
.OrderBy(x =>
{
var t = x.Split('/');
for(int i = 0; i < t.Length; i++) //make all elements in the form NNN
{
t[i] = "000" + t[i];
t[i] = t[i].Substring(t[i].Length - 3);
}
return string.Join(t, "/");
}
)
.ToList();
Mmm.. nasty!

counting highest number of 0's between two 1's in c#

I need help with a program that'll count the maximum number of 0's between two 1's in a given binary number in c#. For example 1100101(binary for 101) the maximum number of 0's between two 1's are 2. Any help?
This the code for counting the 0's in the string but not the 0's between 1's
string bin = "";
int max = 0;
for (int i = rem.Length - 1; i >= 0; i--)
{
if (rem[i] == '0')
{
bin = bin + rem[i];
c++;
}
else
{
bin = bin + rem[i];
}
}
Try this (updated code):
string num = "1011100";
char[] myChar = num.ToCharArray();
bool blFirst = false; //This will check if there is "1" on first element of the input
bool blLast = false; //This will check if there is "1" on last element of the input
if (myChar[0] == '0') //If the condition is true we will remove this on the result
blFirst = true;
if (myChar[myChar.Length - 1] == '0')
blLast = true;
string[] intArr = num.Split('1').ToArray();
List<string> intResult = new List<string>();
//We will make sure that all results only contains '0' and not empty.
intResult = intArr.Where(x => x.All(y => y == '0') && x != string.Empty).Select(x => x).ToList();
if (blFirst == true)
intResult.RemoveAt(0);
if (blLast == true)
intResult.RemoveAt(intResult.Count - 1);
//After all conditions are met (Get only '0' numbers between 1), that's the time we get the maximum count
intOutput = intResult.Select(x => x).Max(x => x.Length);
A simpler version:
string num = "1011100";
//trim the 0's at the start and the end
num=num.Trim(new Char[] { '0' });
string[] intArr = num.Split(new string[] {"1"}, StringSplitOptions.RemoveEmptyEntries);
int Output = intArr.Max(x => x.Length);
This should work:
public static int CountZerosBetweenOnes(string binary)
{
var indicesOfOnes =
binary.Select((c, i) => new {c, i})
.Where(x => x.c == '1')
.Select(x => x.i);
return
indicesOfOnes
.Zip(indicesOfOnes.Skip(1), (a, b) => b - a - 1)
.DefaultIfEmpty(0)
.Max();
}
It does assume that binary does not contain any characters other than 1 and 0.
If you want a version that accepts an int:
public static int CountZerosBetweenOnes(int binary)
{
var indicesOfOnes =
Convert.ToString(binary, 2)
.Select((c, i) => new {c, i})
.Where(x => x.c == '1')
.Select(x => x.i);
return
indicesOfOnes
.Zip(indicesOfOnes.Skip(1), (a, b) => b - a - 1)
.DefaultIfEmpty(0)
.Max();
}
I tested this with the following test code, which outputs the expected result:
public static void Main(string[] args)
{
test("0000000000000"); // Outputs 0
test("0000100010000"); // Outputs 3
test("1111111111111"); // Outputs 0
test("1000000000001"); // Outputs 11
test("1000000000000"); // Outputs 0
test("0000000000001"); // Outputs 0
test("1010101010101"); // Outputs 1
test("1001000100001"); // Outputs 4
}
static void test(string s)
{
Console.WriteLine(CountZerosBetweenOnes(s));
}
Or run it on .Net Fiddle here: https://dotnetfiddle.net/H2Mt8w

how to arrange the item of a list into series arrangement

I have a list of data which contains of random data with combination of string and number:
List<String> Data1 = new List<String>()
{
"1001A",
"1002A",
"1003A",
"1004A",
"1015A",
"1016A",
"1007A",
"1008A",
"1009A",
};
I want this data to arrange into series like this:
1001A - 1004A, 1007A - 1009A, 1015A, 1016A
for every more than 2 counts of data series the output shall be have "-" between the first count and the last count of series, the other non series data will be just added to the last part and all together will separated by ",".
I'd already made some codes only to arrange the data series by the last char of it:
string get_REVISIONMARK = "A";
var raw_serries = arrange_REVISIONSERIES.Where(p => p[p.Length - 1].ToString() == get_REVISIONMARK) .OrderBy(p => p[p.Length - 1) .ThenBy(p => p.Substring(0, p.Length - 1)).ToList();
just ignore the last char I'd already have function for that, and my problem only about the arrangement of the numbers, the length of data is not fixed. for other example of output "1001A - 1005A, 301A, 32A"
I had another sample of my codes this works fine to me, but for me its so lazy code.
for (int c1 = 0; c1 < list_num.Count; c1++)
{
if (list_num[c1] != 0)
{
check1 = list_num[c1];
for (int c2 = 0; c2 < list_num.Count; c2++)
{
if (check1 == list_num[c2])
{
list_num[c2] = 0;
check1 += 1;
list_series.Add(arrange_REVISIONSERIES[c2]);
}
}
check1 = 0;
if (list_series.Count > 2)
{
res_series.Add(list_series[0] + " to " +list_series[list_series.Count - 1]);
list_series.Clear();
}
else
{
if (list_series.Count == 1)
{
res_series.Add(list_series[0]);
list_series.Clear();
}
else
{
res_series.Add(list_series[0] + "," + list_series[1]);
list_series.Clear();
}
}
}
}
var combine_res = String.Join(",", res_series);
MessageBox.Show(combine_res);
this codes work fine for the series number ...
A possible solution (working with current set of values), Please follow the steps below
Declare a class level string list as
public List<String> data_result = new List<string>();
Create a function to iterate through input string list (input string declared inside, named 'data')
public void ArrangeList()
{
List<String> data = new List<string>() { "1001A", "1002A", "1003A",
"1004A", "1015A", "1016A", "1007A", "1008A", "1009A", "1017A" };
List<int> data_int = data.Select(a => Convert.ToInt32(a.Substring(0,
a.Length - 1))).OrderBy(b => b).ToList();
int initializer = 0, counter = 0;
int finalizer = 0;
foreach (var item in data_int)
{
if (initializer == 0)
{ initializer = item; continue; }
else
{
counter++;
if (item == initializer + counter)
finalizer = item;
else
{
LogListing(initializer, finalizer);
initializer = item;
finalizer = item;
counter = 0;
}
}
}
LogListing(initializer, finalizer);
}
Create a function which just logs the result into data_result string list.
public void LogListing(int initializer, int finalizer)
{
if (initializer != finalizer)
{
if (finalizer == initializer + 1)
{
data_result.Add(initializer + "A");
data_result.Add(finalizer + "A");
}
else
data_result.Add(initializer + "A - " + finalizer + "A");
}
else
data_result.Add(initializer + "A");
}
It perfectly generates the result list as
Thumb-up if you like
A linqy solution:
char get_REVISIONMARK = 'A';
var res = arrange_REVISIONSERIES.Select(s => new { Rev = s[s.Length - 1], Value = int.Parse(s.Substring(0, s.Length - 1)), Org = s })
.Where(d => d.Rev == get_REVISIONMARK).OrderBy(d => d.Value)
.Select((val, ind) => new { Index = ind, Org = val.Org, Value = val.Value }).GroupBy(a => a.Value - a.Index)
.Select(gr=>gr.ToList()).OrderBy(l=>l.Count > 2 ? 0 : 1 ).Aggregate(new List<string>(), (list, sublist) =>
{
if (sublist.Count > 2)
list.Add(sublist[0].Org + " - " + sublist[sublist.Count - 1].Org);
else
list.AddRange(sublist.Select(a => a.Org));
return list;
});
The first lines are basically the same as the code you already have (filter on revision and sort), but with the difference that the subvalues are stored in an anonymous type. You could do the same on the pre ordered list, but since splitting the string would be done twice I've included it in the total.
Then a select with index (.Select((val, ind) =>) is made to get value/index pairs. This is done to be able to get the sequences based on an old t-sql row_number trick: for each 'group' the difference between value and index is the same .GroupBy(a => a.Value - a.Index)
After that, normally you'd be as good as done, but since you only want to make sequences of 2 and longer, we make sublists out of the groupby values and do the ordering beforehand to make sure the ranges come for the eventual single elements .Select(gr=>gr.ToList()).OrderBy(l=>l.Count > 2 ? 0 : 1 )
Finally, the list is created of the groups. Several options, but I like to use Aggregate for that. The seed is the resulting list, and the aggregate simply adds to that (where subranges > 2 are cummulated and for single elements and pairs, the single elements are added)
I'm making two assumptions:
The list is already ordered
The non-numeric characters can be ignored
You will get the results in the results variable:
void Main()
{
List<String> Data1 = new List<String>()
{
"1001A",
"1002A",
"1003A",
"1004A",
"1015A",
"1016A",
"1007A",
"1008A",
"1009A",
};
var accu = new List<List<Tuple<int, string>>>();
foreach (var data in Data1)
{
if (accu.Any(t => t.Any(d => d.Item1 == (ToInt(data) - 1))))
{
var item = accu.First(t => t.Any(d => d.Item1 == (ToInt(data) - 1)));
item.Add(new Tuple<int, string>(ToInt(data), data));
}
else
{
accu.Add(new List<Tuple<int, string>>{ new Tuple <int, string>(ToInt(data), data)});
}
}
var results = new List<string>();
results.AddRange(accu.Where(g => g.Count > 2).Select(g => string.Format("{0} - {1}", g.First().Item2, g.Last().Item2)));
results.AddRange(accu.Where(g => g.Count <= 2).Aggregate(new List<string>(), (total, current) => { total.AddRange(current.Select(i => i.Item2)); return total; } ));
}
private static Regex digitsOnly = new Regex(#"[^\d]");
public static int ToInt(string literal)
{
int i;
int.TryParse(digitsOnly.Replace(literal, ""), out i);
return i;
}
So given your starting data:
List<String> arrange_REVISIONSERIES = new List<String>()
{
"1001A",
"1002A",
"1003A",
"1004A",
"1015A",
"1016A",
"1007A",
"1008A",
"1009A",
};
I do this first:
var splits =
arrange_REVISIONSERIES
.Select(datum => new
{
value = int.Parse(datum.Substring(0, datum.Length - 1)),
suffix = datum.Substring(datum.Length - 1, 1),
})
.OrderBy(split => split.suffix)
.ThenBy(split => split.value)
.ToArray();
That's basically the same as your raw_serries, but orders the number part as a number. It seems to me that you need it as a number to make the range part work.
I then do this to compute the groupings:
var results =
splits
.Skip(1)
.Aggregate(
new[]
{
new
{
start = splits[0].value,
end = splits[0].value,
suffix = splits[0].suffix
}
}.ToList(),
(a, s) =>
{
if (a.Last().suffix == s.suffix && a.Last().end + 1 == s.value)
{
a[a.Count - 1] = new
{
start = a.Last().start,
end = s.value,
suffix = s.suffix
};
}
else
{
a.Add(new
{
start = s.value,
end = s.value,
suffix = s.suffix
});
}
return a;
})
.Select(r => r.start == r.end
? String.Format("{0}{1}", r.end, r.suffix)
: (r.start + 1 == r.end
? String.Format("{0}{2}, {1}{2}", r.start, r.end, r.suffix)
: String.Format("{0}{2} - {1}{2}", r.start, r.end, r.suffix)))
.ToArray();
And finally, this to create a single string:
var result = String.Join(", ", results);
That gives me:
1001A - 1004A, 1007A - 1009A, 1015A, 1016A
This code nicely works with data containing different suffixes.

Find MAX Number from a String List with C#

I have a string list having characters with numbers. I just wanted to split the string to get the number and later I need to find the max number from that splitted number list.
Match String
abc
Example List Values
abc9
abc100
abc999
abc
Result
abc1000
I have tried the below code
string Result="";
var SF = (from site in db.SF where site.Code == "xyz" select site.Line2).FirstOrDefault(); // Here I ll get "abc"
int Count = (from Ps in db.Ps where Ps.No.StartsWith(SF) select Ps.No).ToList().Count;
if (Count != 0)
{
var PNo = (from Ps in db.Ps where Ps.No.StartsWith(SF) select Ps.No).ToList().LastOrDefault();
if (PNo != null)
{
int Val = Convert.ToInt32(PNo.Replace(SF, "")) + 1; // Here I need to get `abc1000` based on the above ex. list.
Res = SF + Val.ToString();
}
}
else
{
Result = SF + "1";
}
When I execute the code, It always comes with "abc10" after It reached "abc45". Any help in providing the generic logic will be appreciated.
Thanks
Try below code :
var myStrings = new List<string>();
myStrings.Add("abc9");
myStrings.Add("abc100");
myStrings.Add("abc999");
myStrings.Add("abc");
var maxNumber = "abc" + (from myString in myStrings let value = Regex.Match(myString, #"\d+").Value select Convert.ToInt32(value == string.Empty ? "0" : Regex.Match(myString, #"\d+").Value) + 1).Concat(new[] { 0 }).Max();
Use OrderByDescending() to get the biggest number and then add +1 to result
var result = (from Ps in db.Ps
where Ps.No.StartsWith(SF)
select Ps.No)
.OrderByDescending(m => m.PS.No)
.FirstOrDefault();
How about this? I tried it and it seems to do what you are describing.
public static string testString(string[] input, string startString)
{
int max = 0;
try
{
max = input.Where(s => s.StartsWith(startString) && s.Length > startString.Length)
.Max(s => int.Parse(s.Replace(startString, string.Empty)));
}
catch
{
// no worries, this means max was "abc" without a number
}
return string.Format("{0}{1}", startString, (max + 1).ToString());
}
call it with
string test = testString(new string[] { "abc1", "abc123", "abc23", "xyz23" }, "abc");
try the below mentioned code to get the Max number from your List
var max = myList.Select(v => int.Parse(v.Substring(3))).Max();

Find first index for value in List

In text I have some identical words and I want to get position for each word.
Using such construction:
fullText = File.ReadAllText(fileName);
List<string> arr = fullText.Split(' ').ToList();
List<string> result = arr.
Where(x => string.Equals(x, "set", StringComparison.OrdinalIgnoreCase)).
ToList();
for (int i = 0; i < result.Count; i++)
{
Console.WriteLine(arr.IndexOf(result[i]));
}
I get only last position for each word.For example, I have:
**LOAD SUBCASE1 SUBTITLE2 LOAD SUBCASE3 SUBTITLE4 load Load Load**
and I must get
**LOAD : position 1
LOAD : position 4
load : position 7
Load: position 8
Load : position 8**
To get the index, try something like this;
List<string> result = arr.Select((s,rn) => new {position = rn+1, val = s})
.Where(s => string.Equals(s.val, "LOAD", StringComparison.OrdinalIgnoreCase))
.Select(s => s.val + " : position " + s.position.ToString())
.ToList();
Above query will not return **LOAD and Load**. To get your expected results with ** to the end, I think you could use s.val.Contains() as below;
List<string> result = arr.Select((s, rn) => new { position = rn + 1, val = s })
.Where(s => s.val.ToLower().Contains("load"))
.Select(s =>
s.val.EndsWith("**") ? s.val.Substring(0, s.val.Length - 2) +
" : position " + s.position.ToString() + "**" : s.val + " : position " +
s.position.ToString())
.ToList();
There is no "set" in your wordlist. .... I've mistake, instead must be
"Load"
You are using Equals to compare. That means you want to compare the whole word, not part of the word, hence "**LOAD" would be omitted. Is that desired? Otherwise use IndexOf.
However, you can use this query:
var words = fullText.Split().Select((w, i) => new{Word = w, Index = i});
var matches = words.Where(w => StringComparer.OrdinalIgnoreCase.Equals("load", w.Word));
foreach(var match in matches)
{
Console.WriteLine("Index: {0}", match.Index);
}
DEMO
Index: 4
Index: 7
Index: 8
The IndexOf approach would be:
var partialMatches = words.Where(w => w.Word.IndexOf("load", StringComparison.OrdinalIgnoreCase) != -1);
foreach (var partialMatch in partialMatches)
{
Console.WriteLine("Index: {0}", partialMatch.Index);
}
DEMO
Index: 0
Index: 4
Index: 7
Index: 8
Index: 9
Here's an extension method that does the job:
public static class Extensions
{
public static IEnumerable<int> IndecesOf(this string text, string pattern)
{
var items = text.Split(' ');
for(int i = 0; i < items.Count(); i++)
if(items[i].ToLower().Contains(pattern));
yield return i + 1;
}
}
And the usage:
var fullText = "**LOAD SUBCASE1 SUBTITLE2 LOAD SUBCASE3 SUBTITLE4 load Load Load**";
foreach(int i in fullText.IndecesOf("load"))
Console.WriteLine(i);
Output:
1 4 7 8 9
Please note that I removed the double space from your example-string, when using a double space the split will add an empty string to the array.

Categories

Resources