-6

I want to write a method that divides an array of strings into a n separate arrays, each of which is approximately the size I specify.

For example, if the array I want to divide had 23 elements, then if I specify 7 as the approximate number of elements:

1st array with 8 elements
2nd array with 8 elements 
3rd array with 7 elements

Another example is if there were 100 elements and I specify 18 elements per array, then:

1st array 20 
2nd array 20
3rd array 20
4th array 20
5th array 20

So far, I know that The function needs to return a list of string arrays, but I don't know what to do past that:

private List<string[]> divStrings(int ExpectedStringsPerArray, 
    string[] AllStrings)
{
    // ...
}

The number of arrays will be

 Math.Floor(AllStrings.Count()/ExpectedStringsPerArray) 

How do I divide an array into separate arrays in C#?

7
  • Please try to make your question more specific. This is a very general problem with no obvious real-world application - it smacks of homework. If you cannot make your question any more specific, at least detail what kind of solutions you've tried already. Commented Mar 16, 2013 at 15:26
  • 1
    Have a look at the Besenham algorithm - and assume that the total number of elements is for instance the width and the number of arrays the height. ;) Commented Mar 16, 2013 at 15:26
  • If you have 100 elements, and specify 18 per array, you would not end up with 5 arrays of 20 elements... Commented Mar 16, 2013 at 16:36
  • @DavidKhaykin thats the idea the function has to detect that and make it 20 Commented Mar 16, 2013 at 17:04
  • 1
    @user1590636 How does it get 20? Why not 6 arrays of ~17 items? Commented Mar 20, 2013 at 1:58

4 Answers 4

4

I probably interpret your question wrong, as this seems too easy:

  1. Determine into how many arrays the source array will be divided. (totalCount / elementsPerArray)
  2. For each array:
    1. Determine how many elements to put in it. (elementsRemaining / arraysRemaining)
    2. Copy those elements to a new array.

In code:

private static List<string[]> DivideStrings(int expectedStringsPerArray, string[] allStrings)
{
    List<string[]> arrays = new List<string[]>();

    int arrayCount = allStrings.Length / expectedStringsPerArray;

    int elemsRemaining = allStrings.Length;
    for (int arrsRemaining = arrayCount; arrsRemaining >= 1; arrsRemaining--)
    {
        int elementCount = elemsRemaining / arrsRemaining;

        string[] array = CopyPart(allStrings, elemsRemaining - elementCount, elementCount);
        arrays.Insert(0, array);

        elemsRemaining -= elementCount;
    }

    return arrays;
}

 

private static T[] CopyPart<T>(T[] array, int index, int length)
{
    T[] newArray = new T[length];
    Array.Copy(array, index, newArray, 0, length);
    return newArray;
}

When used like this:

const int count = 23;
const int estimate = 7;
string[] strings = Enumerable.Range(1, count).Select(s => s.ToString()).ToArray();
var list = DivideStrings(estimate, strings);
foreach (var arr in list)
    Console.WriteLine(String.Join(" ", arr));

It prints:

1 2 3 4 5 6 7 8
9 10 11 12 13 14 15 16
17 18 19 20 21 22 23
Sign up to request clarification or add additional context in comments.

Comments

3

You could use Enumerable.Range to get the divided count then select out the items using Linq Skip and Take to select the arrays.

Example:

 public List<string[]> divStrings(int count, string[] array)
 {
    long remainder;
    long divCount = Math.DivRem(array.Count(), count, out remainder);
    int ajustedCount = (int)((divCount > remainder) 
                       ? (divCount / remainder) 
                       : (remainder / divCount)) + count;
    int groupCount = (ajustedCount * divCount) > array.Count() 
        ? (int)divCount
        : (int)divCount++;
    return Enumerable.Range(0, groupCount).Select(g => array.Skip(g * ajustedCount).Take(ajustedCount).ToArray()).ToList();
 }

However

This could be a handy extension method

public static class Extensions
{
    public static List<T[]> SplitEven<T>(this T[] array, int count)
    {
        long remainder;
        long divCount = Math.DivRem(array.Count(), count, out remainder);
        int ajustedCount = (int)((divCount > remainder) 
                           ? (divCount / remainder) 
                           : (remainder / divCount)) + count;
        int groupCount = (ajustedCount * divCount) > array.Count() 
            ? (int)divCount
            : (int)divCount++;
        return Enumerable.Range(0, groupCount).Select(g => array.Skip(g * ajustedCount).Take(ajustedCount).ToArray()).ToList();
    }   
}

Usage:

string[] test = new string[]{};
var result = test.Split<string>(7);

int[] test = new int[]{};
var result = test.Split<int>(7);

7 Comments

I added another extension that may give you the result you are after.
Ok, I think I have added a solution that works for your requirements, sorry I mis-understood the question at first.
@user1590636 In addition to the four I already have (job, moderator, husband, father)? No, but I could probably make one out of it. That said, you might want to look into something that doesn't require the Skip. Because you're using an ICollection<T> implementation (which arrays implement), it's materialized and you have the length. You can figure out the chunk sizes and then deliver back segments of the appropriate size (and you can do it using yield return as well).
@casperOne Thank you really, that comment would have been the best answer.
@user1590636 I was tempted to answer, but I'm too heavily involved in this question as a moderator to allow me to do so.
|
2

Here you go. Just remember you can change lists into arrays with .ToArray(), and arrays into lists with .ToList() which is why I just made them all lists.

List<List<string>> divStrings(int ExpectedStringsPerArray, List<string> AllStrings)
{
    //Set what we're currently up to in the array
    var espa = ExpectedStringsPerArray;
    var ListOfLists = new List<List<string>>();

    //Add the first bunch of elements to the list of lists.
    ListOfLists.Add(AllStrings.Take(ExpectedStringsPerArray).ToList());

    //While we still have elements left to get out
    while (AllStrings.Skip(espa).Take(ExpectedStringsPerArray).Count() != 0)
    {
        //Add the list data we're currently up to to the list of lists
        ListOfLists.Add(AllStrings.Skip(espa).Take(ExpectedStringsPerArray).ToList());
        //Offset what we're up to so next time we're getting the next lot of data
        espa += ExpectedStringsPerArray;
    }

    return ListOfLists;
}

Call this with:

divStrings(30,testList);

3 Comments

-1: This is not correct. In his example of 100 elements, 18 elements per array, he expects 20 20 20 20 20 and you'll give him 18 18 18 18 18 10.
so will all other examples on this page.
If you read the other answers, you'll see that none of them do that.
1

This question is answered. I came across this and answering in my way.

I am not sure what do you mean by approximation and I think that should not be done. You must be sure what should be the size of array you are looking for. Below is a code sample no tested.

List<List<int>> lists = new List<List<int>>();
const int myLimit = 20;

var numbers = (from n in myArray select n).ToList();

while (numbers.Any())
{
    var list = (from number in numbers select number).Take(myLimit).ToList();
    numbers.RemoveRange(0, list.Count());
    lists.Add(list);
}

// Now lists should have list of equal number if items in each. Last one may be an exception.

Hope it helps.

Comments

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.