Odd-Even Index String Sort - c#

Task:
Implement the method at each iteration of which, the odd characters of the string are combined and wrapped to its beginning, and the even characters are wrapped to the end.
"source" The source string.
"count" The count of iterations.
My code:
public static string ShuffleChars(string s, int count)
{
string res = string.Empty;
for (int i = 0; i <= count; i++)
{
res = $"{string.Concat(s.Where((x, i) => i % 2 == 0))}{string.Concat(s.Where((x, i) => i % 2 != 0))}";
}
}
return res;
I sorted string but I don't know how can I do iterations on same value , I tried use "for" , but it is not working, help me pls
i need to sort like this:
1."123456789"
2."135792468" first iteration
3."159483726" second iteration
4."198765432" third iteration
but if I use loop , anyway count = 2 or count = 10 it returns "135792468", I don't know why

The problems with your code are:
You return from inside the loop. This prevents any but the first iteration to complete.
You use <= instead of < in your loop condition. Since we start at 0, this will iterate count + 1 times.
You use the same variable name i for the loop counter as you do in the Where clause, which is illegal since they're in the same scope.
To resolve these issues (and use string.Concat instead of string.Join):
public static string ShuffleChars(string s, int count)
{
for (int i = 0; i < count; i++)
{
s = string.Concat(s.Where((item, index) => index % 2 == 0)) +
string.Concat(s.Where((item, index) => index % 2 != 0));
}
return s;
}
Testing the output:
static void Main()
{
var input = "123456789";
Console.WriteLine($"Starting input = {input}");
Console.WriteLine($"One iteration = {ShuffleChars(input, 1)}");
Console.WriteLine($"Two iterations = {ShuffleChars(input, 2)}");
Console.WriteLine($"Three iterations = {ShuffleChars(input, 3)}");
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output

If I'm reading your problem correctly, you pretty much want to do what your code is doing; shuffle the characters at odd positions to the beginning and even positions to the end.
However, you want to continue to shuffle them, the number of times that you pass in, count. If you try to just loop what you have, you're continuing to use the original string that you passed in, s, and then will always end up returning the same value.
The easiest way to accomplish this is to declare an output string that you continue to assign to until you break out of the loop. So something like:
public static string ShuffleChars(string s, int count)
{
var output = s;
for(int i = 0; i < count; i++) {
output = string.Join("", output.Where((v, j) => j % 2 == 0))
+ string.Join("", output.Where((v, j) => j % 2 != 0));
}
return output;
}
The key here is that you are declaring a new value, output, and initializing it to your string value you passed in. Then for each iteration of the loop, you reassign the value of output to the new value. Finally, once you break out of the loop, you return the final value of output.
As others have stated, there are other ways you could improve on the assignment line. Personally, I probably prefer using string interpolation:
output = $"{output.Where((v, j) => j % 2 == 0)}{output.Where((v, j) => j % 2 != 0)};"

You could convert the string to an IEnumerable<char>, and then apply the same LINQ transformations a count number of times. Finally materialize the IEnumerable<char> to a char[] using the ToArray operator, and then convert the array back to a string.
public static string ShuffleChars(string s, int count)
{
IEnumerable<char> chars = s;
foreach (var _ in Enumerable.Range(0, count))
{
chars = chars
.Select((c, i) => (c, i))
.OrderBy(e => e.i % 2)
.Select(e => e.c);
}
return new String(chars.ToArray());
}

Related

Skipping a range of values in for loop C#

I'm trying to cycle through chars in a string.
string cycleMe = "Hi StackOverflow! Here is my string."
However, I want to skip over certain ranges of indexes. The ranges I want to skip over are stored in a List of objects, delims.
List<Delim> delims = delimCreator();
To retrieve each starting index and ending index for a range, I have to write a loop that accesses each "delim":
delims[0].getFirstIndex() //results in, say, index 2
delims[0].getLastIndex() //results in, say, index 4
delims[1].getFirstIndex() //results in, say, index 5
delims[1].getLastIndex() //results in, say, index 7
(there can be infinitely many "delim" objects in play)
If the above were my list, I'd want to print the string cycleMe, but skip all the chars between 2 and 4 (inclusive) and 5 and 7 (inclusive).
Expected output using the numbers above:
HiOverflow! Here is my string.
Here is the code I have written so far. It loops far more often than I'd expect (it loops ~x2 the number of characters in the string). Thanks in advance! =)
List<Delim> delims = delimAggregateInator(displayTextRaw);
for (int x = 0; x < cycleMe.Length;x++){
for (int i = 0; i < delims.Count; i++){
if (!(x >= delims[i].getFirstIndex() && x <= delims[i].getLastIndex())){
Debug.Log("test");
}
}
I assume that by skipping you meant you want to omit those characters from the original string. If that is the case, you can try Aggregate extension method like below.
string result = delims.Aggregate<Delim, string>(cycleMe, (str, d) => cycleMe = cycleMe.Remove(d.FirstIndex, (d.LastIndex - d.FirstIndex) + 1));
Make sure that the delim list is in the proper order.
Solution might be converting the string to char array, replacing the desired parts to spaces, and converting the output back to string.
Here is the modified version of your code:
string cycleMe = "Hi StackOverflow! Here is my string."
var charArray = cycleMe.ToCharArray(); // Converting to char array
List<Delim> delims = delimAggregateInator(displayTextRaw);
for (int x = 0; x < cycleMe.Length;x++){
for (int i = 0; i < delims.Count; i++){
// ORIGINAL: if (!(x >= delims[i].getFirstIndex() && x <= delims[i].getLastIndex())){
if (x >= delims[i].getFirstIndex() && x <= delims[i].getLastIndex()){
Debug.Log("test");
charArray[x] = ' '; // Replacing the item with space
}
}
string output = new string(charArray); // Converting back to string
P.S. This is probably not the most optimal solution but at least it should work.
You should use LINQ for that
struct Delim
{
public int First { get; set; }
public int Last { get; set; }
}
static void Main(string[] args)
{
string cycleMe = "Hi StackOverflow! Here is my string.";
var delimns = new List<Delim> { new Delim { First=2, Last=4}, new Delim { First = 5, Last = 7 } };
var cut = cycleMe.Where((c, i) =>
!delimns.Any(d => i >= d.First && i <= d.Last));
Console.WriteLine(new string(cut.ToArray());
}
That means I am basically only selecting letters, at positions which are not part of any cutting range.
Also: Fix your naming. A delimiter is a character, not a position (numeric)

IndexOf n-th character of that type

I need to get the index of the n-th character of that type in a string.
Example:
string test = "asdfasdfasdf";
int index = test.IndexOf("a", 3);
and index would be 8, since the 3rd a has the index of 8
Is there a function that does that or do you have any idea how to do that in a smart way?
You can do it in a single line, but it is not pretty:
var s = "aabbabhjhjdsfbaxt";
var idx = s.Select((c, i) => new {c, i})
.Where(p => p.c == 'a')
.Skip(2)
.FirstOrDefault()?.i ?? -1;
The idea is to pair up characters with their indexes, filter by character, skip n-1 items, and take the next one if it exists.
Another approach would be to use regex with look-behind:
var idx = Regex.Match(s, "(?<=(a[^a]*){2})a").Index;
This matches an 'a' preceded by two more 'a's, possibly with other characters in the middle.
A simple for loop will do the trick, but you can place the for loop in an extension method to get this functionality as a one-liner throughout your application.
Like this:
public static int NthIndexOf(this string text, char letter, int occurrence)
{
if (text == null) throw new ArgumentNullException(nameof(text));
int count = 0;
for (int index = 0; index < text.Length; index++)
if (text[index] == letter)
{
if (++count == occurrence)
return index;
}
return -1;
}
Usage:
string test = "asdfasdfasdf";
int index = test.IndexOf('a', 3);
You can create an extension method for this like that:
public static int NthIndexOf(this string s, char c, int occurence)
{
if (s == null) throw new ArgumentNullException(nameof(s));
if (occurence <= 0) return -1;
int i = -1, o = 0;
do
{
i = s.IndexOf(c, i+1);
o++;
} while (i >= 0 && o < occurence);
return i;
}
After checking the input arguments it consecutivly uses string.IndexOf() to get the n-th index of the desired character c.
If no further occurence of c is found (IndexOf returns -1) the loop breaks.
Usage:
string test = "asdfasdfasdf";
int index = test.NthIndexOf('a', 3); // 8
Of course you can do the same with a parameter of type string for c instead of char, but would need to change s.IndexOf(c, i+1) to s.IndexOf(c, i+c.Length).
As René Vogt said you should use an extension method chaining calls to IndexOf() for performance reasons:
public static int NthIndexOf(this string s, string value, int n)
{
var index = -1;
for (; n > 0; n--)
index = s.IndexOf(value, index + 1);
return index;
}
This returns the 0-based start index of the (1-based) nth occurence of the string value.
Here is a working example: https://dotnetfiddle.net/xlqf2B

Count occurences of a string pattern, in a string using Linq

Im working to correctly map links on websites.
I need to be able to count how often ../ occurs in a string. At this moment I have a function that loops through the string and counts, while this works, im looking for a Linq solution.
I know that I can count with a single character like this
int count = Href.Count(f => f == '/');
But, can I, by using LINQ , count how often the pattern ../ occurs? Is this possible?
You can do that nicely with Regex
var dotdotslash=new Regex(#"\.\./");
string test="../../bla/../";
int count=dotdotslash.Matches(test).Count;
↓
3
You could use this extension method:
public static int ContainsCount(this string input, string subString, bool countIntersecting = true, StringComparison comparison = StringComparison.CurrentCulture)
{
int occurences = 0;
int step = countIntersecting ? 1 : subString.Length;
int index = -step;
while ((index = input.IndexOf(subString, index + step, comparison)) >= 0)
occurences++;
return occurences;
}
which returns the number of sub-strings in a given string with pure string-methods:
int count = Href.ContainsCount("../");
String-methods are superior to other methods which use LINQ or regex in terms of efficiency.
This method supports counting intersecting sub-strings(default) and non-overlapping sub-strings.
This shows the difference:
string str = "ottotto";
int count = str.ContainsCount("otto"); // 2
count = str.ContainsCount("otto", false); // 1
Yes, it's possible, but it's very awkward, it will be slow, and it will be hard to read. Don't use it.
How would you count occurrences of a string within a string?
src.Select((c, i) => src.Substring(i)).Count(sub => sub.StartsWith(target))
Alternatively, this looks pretty beautiful:
public static class StringExtensions
{
public static IEnumerable<int> IndexOfAll(this string input, string value){
var currentIndex = 0;
while((currentIndex = input.IndexOf(value, currentIndex)) != -1)
yield return currentIndex++;
}
}
and usage:
"TESTHATEST"
.IndexOfAll("TEST")
.Count()
.Dump();
Regular expression (see Dmitry Ledentsov's answer) is much better here; however Linq is also possible:
String source = #"abc../def../";
// 2
int result = source
.Where((item, index) => source.Substring(index).StartsWith(#"../"))
.Count();
Actually, you can do it in a really LINQy (and awkward :) ) way like this:
private static int CountPatternAppearancesInString(string str, string pattern)
{
var count = str
.Select(
(_, index) =>
index < str.Length - pattern.Length + 1 &&
str.Skip(index)
.Take(pattern.Length)
.Zip(pattern, (strChar, patternChar) => strChar == patternChar)
.All(areEqual => areEqual))
.Count(isMatch => isMatch);
return count;
}
Or, using some of the String-provided methods:
private static int CountPatternAppearancesInString(string str, string pattern)
{
var count = str
.Select(
(_, index) =>
index < str.Length - pattern.Length + 1 &&
str.IndexOf(pattern, index, pattern.Length) >= 0)
.Count(isMatch => isMatch);
return count;
}
But, as already said, it is suboptimal and serves for illustration purpose only.

Find numbers in List<int> with specified difference

For a given a space separated list of numbers, what is the most effecient way of counting the total pairs of numbers which have a difference of N.
e.g. command line in put would be:
5 2
where 5 is the count of numbers to follow and 2 is the difference required
1 5 3 4 2
the 5 numbers to be considered
Output should be
3
because (5,3), (4,2) and (3,1) all have a diff of 2
I can get this algorithm to work, but is there a more efficient way of doing this if you have large sets of numbers to work with? I have incluced three comparison options and the second one should be better than the third but is there something I'm forgetting which could make it much quicker?
private static void Difference()
{
string[] firstInput = SplitInput(Console.ReadLine());
int numberOfNumbers = int.Parse(firstInput[0]);
int diffOfNumbers = int.Parse(firstInput[1]);
string[] secondInput = SplitInput(Console.ReadLine());
List<int> numbers = secondInput.Select(x => Int32.Parse(x)).ToList();
int possibleCombinations = 0;
// Option 1
foreach (int firstNumber in numbers)
{
List<int> compareTo = numbers.GetRange(numbers.IndexOf(firstNumber) + 1, numbers.Count - numbers.IndexOf(firstNumber) - 1);
foreach (int secondNumber in compareTo)
{
int diff = firstNumber - secondNumber;
if (Math.Abs(diff) == diffOfNumbers)
{
possibleCombinations++;
}
}
}
// Option 2
foreach (int firstNumber in numbers)
{
if (numbers.Contains(firstNumber + diffOfNumbers))
{
possibleCombinations++;
}
}
// Option 3
foreach (int firstNumber in numbers)
{
foreach (int secondNumber in numbers)
{
int diff = firstNumber - secondNumber;
if(Math.Abs(diff) == diffOfNumbers)
{
possibleOptions++;
}
}
}
Console.WriteLine(string.Format("Possible number of options are: {0}", possibleCombinations));
Console.ReadLine();
}
private static string[] SplitInput(string input)
{
return input.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
}
If duplicate numbers are not allowed or to be ignored (only count unique pairs), you could use a HashSet<int>:
HashSet<int> myHashSet = ...
int difference = ...
int count;
foreach (int number in myHashSet)
{
int counterpart = number - difference;
if (myHashSet.Contains(counterpart))
{
count++;
}
}
Given the constraints of the problem, where N is the "count of numbers to follow" [1..N], and M is the difference (N=5 and M=2 in the example), why not just return N - M ?
This is done easily with LINQ, allowing for duplicates:
var dict = numbers.GroupBy(n => n).ToDictionary(g => g.Key, g => g.Count());
return dict.Keys.Where(n => dict.ContainsKey(difference-n)).Select(n => dict[difference - n]).Sum();
In the first line we create a dictionary where the keys are the distinct numbers in the input list (numbers) and the values are how many times they appear.
In the second, for each distinct number in the list (equivalent to the keys of the dictioanry) we look to see if the dictionary contains a key for the target number. If so, we add the number of times that target number appeared, which we previously stored as the value for that key. If not we add 0. Finally we sum it all up.
Note in theory this could cause arithmetic overflows if there's no bound other than Int.MinValue and Int.MaxValue on the items in the list. To get around this we need to do a "safe" check, which first makes sure that the difference won't be out of bounds before we try to calculate it. That might look like:
int SafeGetCount(int difference, int number, Dictionary<int,int> dict)
{
if(difference < 0 && number < 0 && int.MinValue - difference > number)
return 0;
if(difference > 0 && number > 0 && int.MaxValue - difference < number)
return 0;
return dict.ContainsKey(difference-number) ? dict[difference - number] : 0;
}
Update
There are a couple of things note entirely clear from your question, like whether you actually want to count duplicate pairs multiple times, and does swapping the numbers count as two different pairs. e.g. if (1,4) is a pair, is (4,1)? My answer above assumes that the answer to both of those questions is yes.
If you don't want to count duplicate pairs multiple times, then go with the HashSet solution from other answers. If you do want to count duplicate pairs but don't want to count twice by swapping the values in the pair, you have to get slightly more complex. E.g.:
var dict = numbers.GroupBy(n => n).ToDictionary(g => g.Key, g => g.Count());
var sum = dict.Keys.Where(n => n*2 != difference)
.Where(n => dict.ContainsKey(difference-n))
.Select(n => dict[difference - n]).Sum()/2;
if(n%2 == 0)
{
sum += dict.ContainsKey(n/2) ? dict[n/2] : 0
}
return sum;
how about sorting the list then iterating over it.
int PairsWithMatchingDifferenceCount(
IEnumerable<int> source,
int difference)
{
var ordered = source.OrderBy(i => i).ToList();
var count = ordered.Count;
var result = 0;
for (var i = 0; i < count - 1; i++)
{
for (var j = i + 1; j < count; j++)
{
var d = Math.Abs(ordered[j] - ordered[i]);
if (d == difference)
{
result++;
}
else if (d > difference)
{
break;
}
}
}
return result;
}
so, as per the example you would call it like this,
PairsWithMatchingDifferenceCount(Enumerable.Range(1, 5), 2);
but, if the sequence generation is a simple as the question suggests why not just.
var m = 5;
var n = 2;
var result = Enumerable.Range(n + 1, m - n)
.Select(x => Tuple.Create(x, x - n)).Count();
or indeed,
var result = m - n;

C#: Cleanest way to divide a string array into N instances N items long

I know how to do this in an ugly way, but am wondering if there is a more elegant and succinct method.
I have a string array of e-mail addresses. Assume the string array is of arbitrary length -- it could have a few items or it could have a great many items. I want to build another string consisting of say, 50 email addresses from the string array, until the end of the array, and invoke a send operation after each 50, using the string of 50 addresses in the Send() method.
The question more generally is what's the cleanest/clearest way to do this kind of thing. I have a solution that's a legacy of my VBScript learnings, but I'm betting there's a better way in C#.
You want elegant and succinct, I'll give you elegant and succinct:
var fifties = from index in Enumerable.Range(0, addresses.Length)
group addresses[index] by index/50;
foreach(var fifty in fifties)
Send(string.Join(";", fifty.ToArray());
Why mess around with all that awful looping code when you don't have to? You want to group things by fifties, then group them by fifties.
That's what the group operator is for!
UPDATE: commenter MoreCoffee asks how this works. Let's suppose we wanted to group by threes, because that's easier to type.
var threes = from index in Enumerable.Range(0, addresses.Length)
group addresses[index] by index/3;
Let's suppose that there are nine addresses, indexed zero through eight
What does this query mean?
The Enumerable.Range is a range of nine numbers starting at zero, so 0, 1, 2, 3, 4, 5, 6, 7, 8.
Range variable index takes on each of these values in turn.
We then go over each corresponding addresses[index] and assign it to a group.
What group do we assign it to? To group index/3. Integer arithmetic rounds towards zero in C#, so indexes 0, 1 and 2 become 0 when divided by 3. Indexes 3, 4, 5 become 1 when divided by 3. Indexes 6, 7, 8 become 2.
So we assign addresses[0], addresses[1] and addresses[2] to group 0, addresses[3], addresses[4] and addresses[5] to group 1, and so on.
The result of the query is a sequence of three groups, and each group is a sequence of three items.
Does that make sense?
Remember also that the result of the query expression is a query which represents this operation. It does not perform the operation until the foreach loop executes.
Seems similar to this question: Split a collection into n parts with LINQ?
A modified version of Hasan Khan's answer there should do the trick:
public static IEnumerable<IEnumerable<T>> Chunk<T>(
this IEnumerable<T> list, int chunkSize)
{
int i = 0;
var chunks = from name in list
group name by i++ / chunkSize into part
select part.AsEnumerable();
return chunks;
}
Usage example:
var addresses = new[] { "a#example.com", "b#example.org", ...... };
foreach (var chunk in Chunk(addresses, 50))
{
SendEmail(chunk.ToArray(), "Buy V14gr4");
}
It sounds like the input consists of separate email address strings in a large array, not several email address in one string, right? And in the output, each batch is a single combined string.
string[] allAddresses = GetLongArrayOfAddresses();
const int batchSize = 50;
for (int n = 0; n < allAddresses.Length; n += batchSize)
{
string batch = string.Join(";", allAddresses, n,
Math.Min(batchSize, allAddresses.Length - n));
// use batch somehow
}
Assuming you are using .NET 3.5 and C# 3, something like this should work nicely:
string[] s = new string[] {"1", "2", "3", "4"....};
for (int i = 0; i < s.Count(); i = i + 50)
{
string s = string.Join(";", s.Skip(i).Take(50).ToArray());
DoSomething(s);
}
I would just loop through the array and using StringBuilder to create the list (I'm assuming it's separated by ; like you would for email). Just send when you hit mod 50 or the end.
void Foo(string[] addresses)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < addresses.Length; i++)
{
sb.Append(addresses[i]);
if ((i + 1) % 50 == 0 || i == addresses.Length - 1)
{
Send(sb.ToString());
sb = new StringBuilder();
}
else
{
sb.Append("; ");
}
}
}
void Send(string addresses)
{
}
I think we need to have a little bit more context on what exactly this list looks like to give a definitive answer. For now I'm assuming that it's a semicolon delimeted list of email addresses. If so you can do the following to get a chunked up list.
public IEnumerable<string> DivideEmailList(string list) {
var last = 0;
var cur = list.IndexOf(';');
while ( cur >= 0 ) {
yield return list.SubString(last, cur-last);
last = cur + 1;
cur = list.IndexOf(';', last);
}
}
public IEnumerable<List<string>> ChunkEmails(string list) {
using ( var e = DivideEmailList(list).GetEnumerator() ) {
var list = new List<string>();
while ( e.MoveNext() ) {
list.Add(e.Current);
if ( list.Count == 50 ) {
yield return list;
list = new List<string>();
}
}
if ( list.Count != 0 ) {
yield return list;
}
}
}
I think this is simple and fast enough.The example below divides the long sentence into 15 parts,but you can pass batch size as parameter to make it dynamic.Here I simply divide using "/n".
private static string Concatenated(string longsentence)
{
const int batchSize = 15;
string concatanated = "";
int chanks = longsentence.Length / batchSize;
int currentIndex = 0;
while (chanks > 0)
{
var sub = longsentence.Substring(currentIndex, batchSize);
concatanated += sub + "/n";
chanks -= 1;
currentIndex += batchSize;
}
if (currentIndex < longsentence.Length)
{
int start = currentIndex;
var finalsub = longsentence.Substring(start);
concatanated += finalsub;
}
return concatanated;
}
This show result of split operation.
var parts = Concatenated(longsentence).Split(new string[] { "/n" }, StringSplitOptions.None);
Extensions methods based on Eric's answer:
public static IEnumerable<IEnumerable<T>> SplitIntoChunks<T>(this T[] source, int chunkSize)
{
var chunks = from index in Enumerable.Range(0, source.Length)
group source[index] by index / chunkSize;
return chunks;
}
public static T[][] SplitIntoArrayChunks<T>(this T[] source, int chunkSize)
{
var chunks = from index in Enumerable.Range(0, source.Length)
group source[index] by index / chunkSize;
return chunks.Select(e => e.ToArray()).ToArray();
}

Categories