C#:将字符串数组划分为 N 个实例 N 项长的最简洁方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1008785/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:25:35  来源:igfitidea点击:

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

c#arraysstringloops

提问by

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.

我有一个电子邮件地址的字符串数组。假设字符串数组的长度是任意的——它可能有几个项目,也可能有很多项目。我想构建另一个由字符串数组中的 50 个电子邮件地址组成的字符串,直到数组的末尾,并在每 50 个之后调用一次发送操作,使用 Send() 方法中的 50 个地址的字符串。

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#.

更普遍的问题是做这种事情的最干净/最清晰的方法是什么。我有一个解决方案,它是我学习 VBScript 的遗产,但我打赌在 C# 中有更好的方法。

采纳答案by Eric Lippert

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.

更新:评论者 MoreCoffee 询问这是如何工作的。假设我们想按三个分组,因为这样更容易输入。

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

假设有九个地址,索引从 0 到 8

What does this query mean?

这个查询是什么意思?

The Enumerable.Rangeis a range of nine numbers starting at zero, so 0, 1, 2, 3, 4, 5, 6, 7, 8.

Enumerable.Range是从零开始九个数字的范围,所以0, 1, 2, 3, 4, 5, 6, 7, 8

Range variable indextakes on each of these values in turn.

范围变量index依次采用这些值中的每一个。

We then go over each corresponding addresses[index]and assign it to a group.

然后我们检查每个对应addresses[index]并将其分配给一个组。

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.

我们将它分配给哪个组?来分组index/3。在 C# 中,整数算术向零舍入,因此索引 0、1 和 2 被 3 除时变为 0。索引 3、4、5 被 3 除时变为 1。索引 6、7、8 变为 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.

因此,我们将addresses[0],addresses[1]和分配addresses[2]给组 0 addresses[3]addresses[4]addresses[5]组 1,以此类推。

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 expressionis a query which represents this operation. It does not performthe operation until the foreachloop executes.

还请记住,查询表达式的结果是表示此操作查询。在循环执行之前它不会执行操作foreach

回答by JaredPar

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

回答by Jon B

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.

我只是遍历数组并使用 StringBuilder 创建列表(我假设它由 ; 分隔,就像你对电子邮件一样)。只需在达到 mod 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)
{
}

回答by dtb

Seems similar to this question: Split a collection into n parts with LINQ?

似乎类似于这个问题:Split a collection into n parts with LINQ?

A modified version of Hasan Khan's answer there should do the trick:

Hasan Khan的答案的修改版本应该可以解决问题:

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[] { "[email protected]", "[email protected]", ...... };

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

回答by Daniel Earwicker

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
}

回答by Erik Funkenbusch

Assuming you are using .NET 3.5 and C# 3, something like this should work nicely:

假设您使用的是 .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);
}

回答by Olseno

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".

我觉得这很简单也足够快。下面的例子将长句子分成15部分,但你可以将batch size作为参数传递,使其动态化。这里我简单地使用“/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);

回答by turdus-merula

Extensions methods based on Eric's answer:

基于Eric 回答的扩展方法:

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