Объединение массивов строк вместе

#c# #.net #arrays #linq

#c# #.net #массивы #linq

Вопрос:

Я хочу объединить содержимое двух массивов строк в новый список, в котором содержимое обоих объединено.

 string[] days = { "Mon", "Tue", "Wed" };
string[] months = { "Jan", "Feb", "Mar" };

// I want the output to be a list with the contents
// "Mon Jan", "Mon Feb", "Mon Mar", "Tue Jan", "Tue Feb" etc...
  

Как я могу это сделать? Для случаев, когда это всего лишь два массива, следующее работает и достаточно просто:

 List<string> CombineWords(string[] wordsOne, string[] wordsTwo)
{
    var combinedWords = new List<string>();
    foreach (var wordOne in wordsOne)
    {
        foreach (string wordTwo in wordsTwo)
        {
            combinedWords.Add(wordOne   " "   wordTwo);
        }
    }
    return combinedWords;
}
  

Но я хотел бы иметь возможность передавать различное количество массивов (т. Е. Иметь метод с подписью ниже) и чтобы он все еще работал.

 List<string> CombineWords(params string[][] arraysOfWords)
{
    // what needs to go here ?
}
  

Или какое-то другое решение было бы отличным. Если это возможно сделать просто с помощью Linq, еще лучше!

Комментарии:

1. params string[] означает «различное количество строк , а не массивов». params string[][] это то, что вам нужно.

2. вы должны изменить сигнатуру своего метода на static List<string> CombineWords(IEnumerable<string[]> arraysOfWords) , потому params string[] arraysOfWords что означает 1 или более параметров типа string

3. Спасибо, ‘params string[] []’ — это то, что я имел в виду. Я отредактировал его, чтобы показать это сейчас.

Ответ №1:

То, что вы хотите сделать, на самом деле является декартовым произведением всех массивов слов, затем соедините слова пробелами. У Эрика Липперта здесь есть простая реализация декартова произведения Linq. Вы можете использовать его для реализации CombineWords:

 List<string> CombineWords(params string[][] arraysOfWords)
{
    return CartesianProduct(arraysOfWords)
            .Select(x => string.Join(" ", x))
            .ToList();
}
  

Ответ №2:

Для перекрестного соединения с любым количеством массивов строк:

 // Define other methods and classes here
List<string> CombineWords(params string[][] arraysOfWords)
{
    if (arraysOfWords.Length == 0)
        return new List<string>();

    IEnumerable<string> result = arraysOfWords[0];

    foreach( string[] words in arraysOfWords.Skip(1) )
    {
        var tempWords = words;

        result = from r in result
                 from w in tempWords 
                 select string.Concat(r, " ", w);
    }

    return result.ToList();
}
  

Комментарии:

1. Спасибо, но, похоже, это дает меньшие результаты при передаче 3 или более массивов по сравнению с ответами Томаса Левеска или Лукасуса.

2. Спасибо, что указали на это. Изменение, которое я внес, чтобы удалить дополнительные вызовы ToList, фактически нарушило его, временная переменная требуется из-за отложенного выполнения, я полагаю. @Lucasus уже учел это, его хороший ответ 🙂 Обновил свой ответ, чтобы исправить ошибку и избежать путаницы.

Ответ №3:

Приведенный ниже код работает для любого количества массивов (и в некоторой степени использует linq):

 List<string> CombineWords(params string[][] wordsToCombine)
{
     if (wordsToCombine.Length == 0)
         return new List<string>();

     IEnumerable<string> combinedWords = wordsToCombine[0].ToList();
     for (int i = 1; i < wordsToCombine.Length;   i)
     {
         var temp = i;
         combinedWords = (from x in combinedWords from y in wordsToCombine[temp]
                       select x   " "   y);
     }
     return combinedWords.ToList();
 }
  

Комментарии:

1. Snap, похоже, мы придумали что-то похожее 🙂

2. После рефакторинга это не понадобится. Вызовы ToList() выполняются очень быстро (я сравнил его в два раза быстрее, чем решение Томаса Левеска), так что это именно то, что я искал — большое спасибо!

Ответ №4:

 public static List<string> CombineWords(params string[][] arraysOfWords)
{
    var strings = new List<string>();

    if (arraysOfWords.Length == 0)
    {
        return strings;
    }

    Action<string, int> combineWordsInternal = null;

    combineWordsInternal = (baseString, index) =>
    {
        foreach (var str in arraysOfWords[index])
        {
            string str2 = baseString   " "   str;

            if (index   1 < arraysOfWords.Length)
            {
                combineWordsInternal(str2, index   1);
            }
            else
            {
                strings.Add(str2);
            }
        }
    };

    combineWordsInternal(string.Empty, 0);

    return strings;
}
  

Вторая попытка… Я не могу сделать это в LINQ… Слишком сложно для правильной компоновки 🙂

Я использую локальную анонимную функцию (и показываю, что повторять анонимные функции довольно сложно, потому что вы должны объявлять их отдельно).

Комментарии:

1. Возможно, вы захотите еще раз прочитать вопрос, ваш ответ не о том, о чем просил @mikel. Вопрос не в объединении.

2. @ANeves Я против внутренних методов. НО я могу использовать локальные функции 🙂 🙂

3. Немного более запутанный, что означает менее удобный в обслуживании, но работает для меня.

4. @ANeves Это зависит… Если у вас есть Javascript «lineage», это настолько ясно, насколько это возможно 🙂

Ответ №5:

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

   var s1 = new string[] { "A", "B", "C" };
  var s2 = new string[] { "1", "2", "3", "4" };
  var s3 = new string[] { "-", "!", "?" };
  var res = Combine(s1, s2, s3);
  

И функция, о которой идет речь:

 private List<string> Combine(params string[][] arrays)
{
    if (arrays.Length == 1)
    {
        // The trivial case - exit.
        return new List<string>(arrays[0]);
    }
    IEnumerable<string> last = arrays[arrays.Length - 1];
    // Build from the last array, progress forward
    for (int i = arrays.Length - 2; i >= 0; i--)
    {
        var buffer = new List<string>();
        var current = arrays[i];
        foreach (var head in current)
        {
            foreach (var tail in last)
            {
                // Concatenate with the desired space.
                buffer.Add(head   " "   tail);
            }
        }
        last = buffer;
    }
    return (List<string>)last;
}
  

Ответ №6:

Не могли бы вы попробовать этот метод?

 static List<string> CombineWords(string[] wordsOne, string[] wordsTwo)
{
    var combinedWords = new List<string>();
    for(int x = 0; (x <= wordsOne.Length - 1);   x)
    {
        for(int y = 0; (x <= wordsTwo.Length - 1);   y)
        {
            combinedWords.Add(string.Format("{0} {1}", wordsOne[x], wordsTwo[y]));
        }
    }
    return combinedWords;
}
  

Крис