0

I want to split the 'tmpstr' members based on a 'tmpnumstr' members. here is my temporary solution , but i am looking for the general solution for arbitrary 'tmpstr' and 'tmpnumstr'.

string tmpstr = "New,Open,Exit,Copy,Cut,Paste,Help,About,"; 
string tmpnumstr = "3,3,2,"; // string of numbers

string[] tmpstrArray = tmpstr.TrimEnd(',').Split(',');

for (int i = 0; i < 3; i++)
{
    textBox1.Text += tmpstrArray[i] + @",";
}
textBox1.Text += Environment.NewLine;
for (int i = 3; i < 6; i++)
{
    textBox1.Text += tmpstrArray[i] + @",";
}
textBox1.Text += Environment.NewLine;
for (int i = 6; i < 8; i++)
{
    textBox1.Text += tmpstrArray[i] + @",";
}
textBox1.Text += Environment.NewLine;

The output is ==>
New,Open,Exit,
Copy,Cut,Paste,
Help,About,

3 Answers 3

5

Sounds like you want to split the raw string to several arrays, based on dynamic input?

In such case, you should split the words to a flat list first, then use combination of .Skip() and .Take() to split it further to sub groups.

Sample code using your existing code as its base:

string rawWords = "New,Open,Exit,Copy,Cut,Paste,Help,About,";
string rawGroupCounts = "3,3,2,"; // string of numbers
List<int> groupCounts = rawGroupCounts.Split(new char[] { ',' }, 
    StringSplitOptions.RemoveEmptyEntries).
    ToList().ConvertAll(rawValue => Int32.Parse(rawValue));
List<string> words = rawWords.Split(new char[] { ',' }, 
    StringSplitOptions.RemoveEmptyEntries).ToList();
List<List<string>> wordGroups = new List<List<string>>();
if (groupCounts.Count > 0)
{
    int skipCounter = 0;
    groupCounts.ForEach(count =>
    {
        wordGroups.Add(words.Skip(skipCounter).Take(count).ToList());
        skipCounter += count;
    });
}
textBox1.Text = string.Join(Environment.NewLine, 
    wordGroups.ConvertAll(group => string.Join(", ", group)));

(I have also changed the variable names to have some meaning)

Sign up to request clarification or add additional context in comments.

3 Comments

u-p-v-o-t-e for you for your nice answer
Thanks, guess it means it's working for you. Cheers!
very nice solution
1

I believe this should do it.

string tmpstr = "New,Open,Exit,Copy,Cut,Paste,Help,About,"; 
string tmpnumstr = "3,3,2,"; // string of numbers

string[] tmpstrArray = tmpstr.TrimEnd(',').Split(',');
int[] tmpnumstrArray = tmpnumstr.TrimEnd(',').Split(',').Select(x => int.Parse(x)).ToArray();       //To convert tmpnumstr to integer array

for(int i=1;i<tmpnumstrArray.Length;i++)
    tmpnumstrArray[i] += tmpnumstrArray[i - 1];

for(int i = 0, k = 0;i < tmpnumstrArray[tmpnumstrArray.Length - 1];i++)
{
    if(i == tmpnumstrArray[k])
    {
        textBox1.Text += Environment.NewLine;
        k++;
    }
    textBox1.Text += tmpstrArray[i] + @",";
}
textBox1.Text += Environment.NewLine;

1 Comment

u-p-v-o-t-e for you for your answer but you need to fix ' Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<int>' to 'int[]'. An explicit conversion exists (are you missing a cast?) ' in your answer :)
0

Given the following helper classes for "sugar":

public class BatchingProcessor<T>
{
    private readonly IEnumerator<T> enumerator;

    public BatchingProcessor(IEnumerable<T> enumerable)
    {
        this.enumerator = enumerable.GetEnumerator();
    }

    public Batch<T> Take(int count)
    {
        var values = this.TakeUntilEndIsReached(count).ToArray();
        return new Batch<T>(values, count);
    }

    private IEnumerable<T> TakeUntilEndIsReached(int count)
    {
        for (int i = 0; i < count; i++)
        {
            if (enumerator.MoveNext())
            {
                yield return enumerator.Current;
            }
            else
            {
                break;
            }
        }
    }
}

public class Batch<T>
{
    private readonly T[] values;
    private readonly int batchSize;

    public Batch(T[] values, int batchSize)
    {
        this.values = values;
        this.batchSize = batchSize;
    }

    public T[] Values => this.values;

    public int BatchSize => this.batchSize;

    public bool EndReached => this.values.Length < this.batchSize;
}

We can write:

string tmpstr = "New,Open,Exit,Copy,Cut,Paste,Help,About,";
string tmpnumstr = "3,3,2,"; // string of numbers;

string[] items = tmpstr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
int[] itemSequence = tmpnumstr
    .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
    .Select(x => Convert.ToInt32(x))
    .ToArray();

var sb = new StringBuilder();
var batchingProcessor = new BatchingProcessor<string>(items);
foreach (int itemCount in itemSequence)
{
    var batch = batchingProcessor.Take(itemCount);

    foreach (string item in batch.Values)
    {
        sb.Append(item);
        sb.Append(",");
    }
    sb.AppendLine();

    if (batch.EndReached)
    {
        // tmpnumstr specifies more strings than tmpstr contains.
        break;
    }
}

textBox1.Text = sb.ToString();

i've tested that it results in exactly the same string as your original code:

string expectedString = @"New,Open,Exit,
Copy,Cut,Paste,
Help,About,
";

sb.ToString().Should().Be(expectedString);

Now, if you're sure that the item count and group specification matches, we don't need to check for that and can do:

string tmpstr = "New,Open,Exit,Copy,Cut,Paste,Help,About,";
string tmpnumstr = "3,3,2,"; // string of numbers;

string[] items = tmpstr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
int[] itemSequence = tmpnumstr
    .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
    .Select(x => Convert.ToInt32(x))
    .ToArray();

var batchingProcessor = new BatchingProcessor<string>(items);

var batches = itemSequence
    .Select(itemCount => batchingProcessor.Take(itemCount))
    .Select(batch => string.Join(",", batch.Values))
    .ToArray();

textBox1.Text = string.Join("," + Environment.NewLine, batches);

4 Comments

All of this just to avoid using list of lists? Or is there something else I'm missing?
@ShadowWizard The BatchProcessor<T> is a helper class that i use from time to time. In my opinion it allows to write more concise code. I thought of writing a Skip(), Take() solution first but if there's a large dataset this requires a lot of enumerator-processing (one's skipping the same elements many times).
True, but it looks like using a 5 kilo hammer to put a small nail into the wall. :-)
@ShadowWizard true if you never ever need the BatchProcessor<T> again then it's quite a lot of code for that problem. However i believe clean-code is worth something, too ;-) Also, if i see this right, the currently accepted solutions cannot handle not-matching arrays "gracefully". Now, granted, i'm a big believer in "fail fast", too, so hiding issues is a decision which should not be taken lightly...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.