C #: Самый простой способ разделить массив строк на N экземпляров длиной N элементов - PullRequest
21 голосов
/ 17 июня 2009

Я знаю, как сделать это безобразно, но мне интересно, есть ли более элегантный и лаконичный метод.

У меня есть строковый массив адресов электронной почты. Предположим, что массив строк имеет произвольную длину - он может содержать несколько элементов или может содержать очень много элементов. Я хочу построить еще одну строку, состоящую из, скажем, 50 адресов электронной почты из массива строк, до конца массива, и вызывать операцию отправки после каждых 50, используя строку из 50 адресов в методе Send ().

Вопрос в целом заключается в том, какой самый чистый / ясный способ сделать подобные вещи. У меня есть решение, которое является наследием моих знаний VBScript, но я держу пари, что в C # есть лучший способ.

Ответы [ 8 ]

49 голосов
/ 17 июня 2009

Вы хотите элегантный и лаконичный, я дам вам элегантный и лаконичный:

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

Зачем возиться со всем этим ужасным цикличным кодом, когда это не нужно? Вы хотите сгруппировать вещи по пятидесятым, , а затем сгруппировать по пятидесятым. Для этого и нужен групповой оператор!

ОБНОВЛЕНИЕ: комментатор MoreCoffee спрашивает, как это работает. Давайте предположим, что мы хотели сгруппировать по три, потому что это легче набирать.

var threes = from index in Enumerable.Range(0, addresses.Length) 
              group addresses[index] by index/3;

Предположим, что существует девять адресов, проиндексированных от нуля до восьми

Что означает этот запрос?

Enumerable.Range - это диапазон из девяти чисел, начинающихся с нуля, поэтому 0, 1, 2, 3, 4, 5, 6, 7, 8.

Переменная диапазона index принимает каждое из этих значений по очереди.

Затем мы просматриваем каждый соответствующий addresses[index] и назначаем его группе.

В какую группу мы его назначаем? К группе index/3. Целочисленные арифметические округления до нуля в C #, поэтому индексы 0, 1 и 2 становятся 0 при делении на 3. Индексы 3, 4, 5 становятся 1, когда делятся на 3. Индексы 6, 7, 8 становятся 2.

Поэтому мы присваиваем addresses[0], addresses[1] и addresses[2] группе 0, addresses[3], addresses[4] и addresses[5] группе 1 и т. Д.

Результатом запроса является последовательность из трех групп, а каждая группа представляет собой последовательность из трех элементов.

Это имеет смысл?

Помните также, что результатом выражения запроса является запрос , представляющий эту операцию . не выполняет операцию до тех пор, пока не будет выполнен цикл foreach.

14 голосов
/ 17 июня 2009

Похоже на этот вопрос: Разделить коллекцию на n частей с помощью LINQ?

Модифицированная версия ответа Хасана Хана , который должен решить эту проблему:

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

Пример использования:

var addresses = new[] { "a@example.com", "b@example.org", ...... };

foreach (var chunk in Chunk(addresses, 50))
{
    SendEmail(chunk.ToArray(), "Buy V14gr4");
}
2 голосов
/ 17 июня 2009

Если вы используете .NET 3.5 и C # 3, что-то вроде этого должно хорошо работать:

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);
}
2 голосов
/ 17 июня 2009

Похоже, что вход состоит из отдельных строк адреса электронной почты в большом массиве, а не нескольких адресов электронной почты в одной строке, верно? И в выходных данных каждая партия представляет собой одну объединенную строку.

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
}
1 голос
/ 17 июня 2009

Я бы просто перебрал массив и использовал StringBuilder для создания списка (я предполагаю, что он разделен; как вы сделали бы для электронной почты) Просто отправьте, когда вы нажмете мод 50 или конец.

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)
{
}
0 голосов
/ 26 ноября 2017

Методы расширения, основанные на Ответ Эрика :

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();
}
0 голосов
/ 12 марта 2015

Я думаю, что это просто и достаточно быстро. В приведенном ниже примере длинное предложение делится на 15 частей, но вы можете передать размер пакета как параметр, чтобы сделать его динамичным.

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

Показывает результат операции разделения.

 var parts = Concatenated(longsentence).Split(new string[] { "/n" }, StringSplitOptions.None);
0 голосов
/ 17 июня 2009

Я думаю, нам нужно немного больше узнать о том, как именно выглядит этот список, чтобы дать окончательный ответ. Сейчас я предполагаю, что это список адресов электронной почты, разделенный точкой с запятой. Если это так, вы можете сделать следующее, чтобы получить разделенный список.

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;
     }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...