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

Присоединить строку, используя разделители

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

EDIT: я не использовал StringBuilder, чтобы сделать код немного проще.

Использовать цикл for

for(int i=0; i < list.Length; i++)
{
    result += list[i];
    if(i != list.Length - 1)
        result += delimiter;
}

Используйте параметр For Loop для установки первого элемента ранее

result = list[0];
for(int i = 1; i < list.Length; i++)
    result += delimiter + list[i];

Они не будут работать для IEnumerable, если вы заранее не знаете длину списка, поэтому

Использование цикла foreach

bool first = true;
foreach(string item in list)
{
    if(!first)
        result += delimiter;
    result += item;
    first = false;
}

Изменение в цикле foreach

От решения Jon

StringBuilder builder = new StringBuilder();
string delimiter = "";
foreach (string item in list)
{
    builder.Append(delimiter);
    builder.Append(item);
    delimiter = ",";       
}
return builder.ToString();

Использование Iterator

Снова от Джона

using (IEnumerator<string> iterator = list.GetEnumerator())
{
    if (!iterator.MoveNext())
        return "";
    StringBuilder builder = new StringBuilder(iterator.Current);
    while (iterator.MoveNext())
    {
        builder.Append(delimiter);
        builder.Append(iterator.Current);
    }
    return builder.ToString();
}

Какие еще существуют алгоритмы?

4b9b3361

Ответ 1

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

В С# вы можете использовать:

StringBuilder builder = new StringBuilder();
string delimiter = "";
foreach (string item in list)
{
    builder.Append(delimiter);
    builder.Append(item);
    delimiter = ",";       
}
return builder.ToString();

Это добавит запятую на все, кроме первого элемента. Аналогичный код будет полезен и на Java.

EDIT: здесь альтернатива, немного похожая на Ian, позже отвечает, но работает над общим IEnumerable<string>.

// Change to IEnumerator for the non-generic IEnumerable
using (IEnumerator<string> iterator = list.GetEnumerator())
{
    if (!iterator.MoveNext())
    {
        return "";
    }
    StringBuilder builder = new StringBuilder(iterator.Current);
    while (iterator.MoveNext())
    {
        builder.Append(delimiter);
        builder.Append(iterator.Current);
    }
    return builder.ToString();
}

ИЗМЕНИТЬ почти через 5 лет после первоначального ответа...

В .NET 4, string.Join был значительно перегружен. Там происходит перегрузка IEnumerable<T>, которая автоматически вызывает ToString, и есть перегрузка для IEnumerable<string>. Так что вам больше не нужен код выше... для .NET, в любом случае.

Ответ 2

В .NET вы можете использовать метод String.Join:

string concatenated = String.Join(",", list.ToArray());

Используя .NET Reflector, мы можем узнать, как он это делает:

public static unsafe string Join(string separator, string[] value, int startIndex, int count)
{
    if (separator == null)
    {
        separator = Empty;
    }
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    if (startIndex < 0)
    {
        throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
    }
    if (count < 0)
    {
        throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
    }
    if (startIndex > (value.Length - count))
    {
        throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
    }
    if (count == 0)
    {
        return Empty;
    }
    int length = 0;
    int num2 = (startIndex + count) - 1;
    for (int i = startIndex; i <= num2; i++)
    {
        if (value[i] != null)
        {
            length += value[i].Length;
        }
    }
    length += (count - 1) * separator.Length;
    if ((length < 0) || ((length + 1) < 0))
    {
        throw new OutOfMemoryException();
    }
    if (length == 0)
    {
        return Empty;
    }
    string str = FastAllocateString(length);
    fixed (char* chRef = &str.m_firstChar)
    {
        UnSafeCharBuffer buffer = new UnSafeCharBuffer(chRef, length);
        buffer.AppendString(value[startIndex]);
        for (int j = startIndex + 1; j <= num2; j++)
        {
            buffer.AppendString(separator);
            buffer.AppendString(value[j]);
        }
    }
    return str;
}

Ответ 3

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

Python.
",".join(sequence)

Подробнее см. документацию о присоединении.

Ответ 4

Для python убедитесь, что у вас есть список строк, иначе ','. join (x) завершится с ошибкой. Для безопасного метода с использованием 2,5 +

delimiter = '","'
delimiter.join(str(a) if a else '' for a in list_object)

"str (a), если" else "хорош для типов None, иначе str() заканчивает создание" None", что не приятно;)

Ответ 5

В PHP implode():

$string = implode($delim, $array);

Ответ 6

Я всегда добавляю делиметр, а затем удаляю его в конце, если необходимо. Таким образом, вы не выполняете оператор if для каждой итерации цикла, когда вы только заботитесь о выполнении работы один раз.

StringBuilder sb = new StringBuilder();

foreach(string item in list){
    sb.Append(item);
    sb.Append(delimeter);
}

if (list.Count > 0) {
    sb.Remove(sb.Length - delimter.Length, delimeter.Length)
}

Ответ 7

Я бы сказал это рекурсивно.

  • Проверьте, является ли число аргументов строки равным 1. Если это так, верните его.
  • В противном случае рекурсия, но объедините первые два аргумента с разделителем между ними.

Пример в Common Lisp:

(defun join (delimiter &rest strings)
  (if (null (rest strings))
      (first strings)
      (apply #'join
             delimiter
             (concatenate 'string
                          (first strings)
                          delimiter
                          (second strings))
             (cddr strings))))

Более идиоматичным способом является использование reduce, но это расширяется почти до тех же инструкций, что и выше:

(defun join (delimiter &rest strings)
  (reduce (lambda (a b)
            (concatenate 'string a delimiter b))
          strings))

Ответ 8

List<string> aaa = new List<string>{ "aaa", "bbb", "ccc" };
string mm = ";";
return aaa.Aggregate((a, b) => a + mm + b);

и вы получите

aaa;bbb;ccc

lambda довольно удобна

Ответ 9

Проблема состоит в том, что на компьютерных языках редко имеется строка booleans, то есть методы, которые имеют строку типа, которая делает что-нибудь полезное. SQL Server по крайней мере имеет [null] null и nullif, которые при объединении решают проблему разделителя, кстати: isnotnull (nullif (columnvalue, "),"," + columnvalue))

Проблема заключается в том, что в языках есть булевы, и есть строки, и ни одна из них не будет встречаться, кроме как в уродливых формах кодирования, например

concatstring = string1 + "," + string2; если (fubar)   concatstring + = string3 concatstring + = string4 и т.д.

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

Джонатан

Ответ 11

Поскольку вы отметили этот язык агностиком,

Вот как вы это сделаете в python

# delimiter can be multichar like "| trlalala |"
delimiter = ";"
# sequence can be any list, or iterator/generator that returns list of strings
result = delimiter.join(sequence)
#result will NOT have ending delimiter 

Изменить: Я вижу, что несколько человек ответили на ответ. Извините за дублирование

Ответ 12

Это рабочее решение в С#, в Java, вы можете использовать схожие для каждого на итераторе.

        string result = string.Empty; 

        // use stringbuilder at some stage.
        foreach (string item in list)
            result += "," + item ;

        result = result.Substring(1);
        // output:  "item,item,item"

При использовании .NET вы можете использовать метод расширения, чтобы вы могли делать list.ToString( "") Подробнее см. Разделитель разделителя ToString для массива, списка, словаря, общего IEnumerable

// contains extension methods, it must be a static class.
public static class ExtensionMethod
{
    // apply this extension to any generic IEnumerable object.
    public static string ToString<T>(this IEnumerable<T> source,
      string separator)
    {
        if (source == null)
           throw new ArgumentException("source can not be null.");

        if (string.IsNullOrEmpty(separator))
           throw new ArgumentException("separator can not be null or empty.");

        // A LINQ query to call ToString on each elements
        // and constructs a string array.
        string[] array =
         (from s in source
          select s.ToString()
          ).ToArray();

        // utilise builtin string.Join to concate elements with
        // customizable separator.
        return string.Join(separator, array);
    }
}

EDIT: по соображениям производительности замените код конкатенации на решение построителя строк, указанное в этом потоке.

Ответ 13

Я предпочитаю лучший способ сделать что-то вроде этого (я буду использовать псевдокод, поэтому мы сделаем это по-настоящему языковым агностиком):

function concat(<array> list, <boolean> strict):
  for i in list:
    if the length of i is zero and strict is false:
      continue;
    if i is not the first element:
      result = result + separator;
    result = result + i;
  return result;

второй аргумент concat(), strict - это флаг, чтобы знать, нужно ли рассматривать возможные пустые строки в конкатенации или нет.

Я привык не рассматривать добавление окончательного разделителя; с другой стороны, если строгая ложь, результирующая строка может быть свободной от таких вещей, как "A, B,, F", если разделитель является запятой, но вместо этого будет представлен как "A, B, F".

Ответ 14

Увидите ответ Python как 3 раза, но не Ruby?!?!?

первая часть кода объявляет новый массив. Затем вы можете просто вызвать метод .join() и передать разделитель, и он вернет строку с разделителем в середине. Я считаю, что метод join вызывает метод .to_s для каждого элемента перед его объединением.

["ID", "Description", "Active"].join(",")
>> "ID, Description, Active"

это может быть очень полезно при объединении метапрограмм с взаимодействием с базой данных.

Кто-нибудь знает, есть ли у С# что-то похожее на этот синтаксический сахар?

Ответ 15

В Java 8 мы можем использовать:

List<String> list = Arrays.asList(new String[] { "a", "b", "c" });
System.out.println(String.join(",", list)); //Output: a,b,c

Чтобы иметь префикс и суффикс, мы можем сделать

StringJoiner joiner = new StringJoiner(",", "{", "}");
list.forEach(x -> joiner.add(x));
System.out.println(joiner.toString()); //Output: {a,b,c}

До Java 8 вы можете сделать, как ответить Jon

StringBuilder sb = new StringBuilder(prefix);
boolean and = false;
for (E e : iterable) {        
    if (and) {
        sb.append(delimiter);
    }
    sb.append(e);
    and = true;
}
sb.append(suffix);

Ответ 16

string result = "";
foreach(string item in list)
{
    result += delimiter + item;
}
result = result.Substring(1);

Изменить: Конечно, вы не использовали бы этот или любой из ваших алгоритмов для объединения строк. С С#/.NET вы, вероятно, используете StringBuilder:

StringBuilder sb = new StringBuilder();
foreach(string item in list)
{
    sb.Append(delimiter);
    sb.Append(item);
}
string result = sb.ToString(1, sb.Length-1);

И вариация этого решения:

StringBuilder sb = new StringBuilder(list[0]);
for (int i=1; i<list.Count; i++)
{
    sb.Append(delimiter);
    sb.Append(list[i]);
}
string result = sb.ToString();

Оба решения не содержат проверок ошибок.

Ответ 17

что как python решает проблему:

','.join(list_of_strings)

Я никогда не мог понять необходимость "алгоритмов" в тривиальных случаях, хотя

Ответ 18

Вы можете написать собственный метод AppendTostring (string, delimiter), который добавляет разделитель тогда и только тогда, когда строка не пуста. Затем вы просто вызываете этот метод в любом цикле, не беспокоясь о том, когда добавлять и не добавлять.

Изменить: еще лучше, конечно, использовать метод StringBuffer в методе, если он доступен.

Ответ 19

В .NET я использовал бы метод String.join, если это возможно, что позволяет указать разделитель и строковый массив. Список может быть преобразован в массив с помощью ToArray, но я не знаю, какой будет удар по производительности.

Три упомянутых вами алгоритма - это то, что я хотел бы использовать (мне нравится второй, потому что в нем нет оператора if, но если длина неизвестна, я бы использовал третий, потому что он не дублирует код), Второй будет работать, только если список не пуст, поэтому может потребоваться другое оператор if.

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

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

Ответ 20

От http://dogsblog.softwarehouse.co.zw/post/2009/02/11/IEnumerable-to-Comma-Separated-List-(and-more).aspx

Домашнее животное ненавидят меня при разработке, делает список разделенных запятыми идентификаторов, он очень прост, но всегда имеет уродливый код.... Общие решения состоят в том, чтобы прокручивать и помещать запятую после каждого элемента, а затем удалять последний символ, или иметь оператор if, чтобы проверить, находитесь ли вы в начале или в конце списка. Ниже приведено решение, которое вы можете использовать в любом IEnumberable, например в List, Array и т.д. Это также самый эффективный способ, которым я могу думать об этом, поскольку он полагается на назначение, которое лучше, чем редактирование строки или использование if.

public static class StringExtensions
{
    public static string Splice<T>(IEnumerable<T> args, string delimiter)
    {
        StringBuilder sb = new StringBuilder();
        string d = "";
        foreach (T t in args)
        {
            sb.Append(d);
            sb.Append(t.ToString());
            d = delimiter;
        }
        return sb.ToString();
    }
}

Теперь он может использоваться с любым IEnumerable, например.

StringExtensions.Splice(billingTransactions.Select(t = > t.id), "," )

чтобы дать нам 31,32,35

Ответ 21

Для java в этом question был указан очень полный ответ или этот question.

Это использование StringUtils.join в Apache Commons

String result = StringUtils.join(list, ", ");

Ответ 22

В Clojure вы можете просто использовать clojure.contrib.str-utils/str-join:

(str-join ", " list)

Но для реального алгоритма:

(reduce (fn [res cur] (str res ", " cur)) list)

Ответ 23

Groovy также имеет метод String Object.join(String).

Ответ 24

Java (от решения Jon):

    StringBuilder sb = new StringBuilder();
    String delimiter = "";
    for (String item : items) {
        sb.append(delimiter).append(item);
        delimeter = ", ";
    }
    return sb.toString();