Подтвердить что ты не робот

С#: Самый чистый способ разделить массив строк на N экземпляров N элементов длинный

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

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

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

4b9b3361

Ответ 1

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

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. Целые арифметические раунды к нулю в С#, поэтому индексы 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 не выполнится.

Ответ 2

Похоже на этот вопрос: Разделить коллекцию на 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[] { "[email protected]", "[email protected]", ...... };

foreach (var chunk in Chunk(addresses, 50))
{
    SendEmail(chunk.ToArray(), "Buy V14gr4");
}

Ответ 3

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

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
}

Ответ 4

Предполагая, что вы используете .NET 3.5 и С# 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);
}

Ответ 5

Я бы просто пропустил массив и использовал 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)
{
}

Ответ 6

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

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

Ответ 7

Я думаю, что это достаточно просто и достаточно быстро. Пример ниже делит длинное предложение на 15 частей, но вы можете передать размер партии в качестве параметра, чтобы сделать его динамическим. Вот я просто делю с помощью "/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;
 }

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

 var parts = Concatenated(longsentence).Split(new string[] { "/n" }, StringSplitOptions.None);

Ответ 8

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

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