Вызов Eric Lippert “двусмысленный запятой”, лучше всего ответьте?

Решил это. Причина, по которой ResolveEnd не запускался, заключалась в том, что я пропустил `this.tranDetailSubject.complete (); в Send Detail () сразу после next ().

SendDetail ():

sendDetail(id: number) {
console.log("Snd trandetail");

const url = this.rootUrl + 'api/Details/Select?ID=' + id;

this.http.get(url).pipe(
  retry (3)
).toPromise()
.then((data: any) => {
    this.detailSubject.next(data.Entity);
    this.detailSubject.complete(); //Tells compiler to trigger ResolveEnd because observable is complete now.

});
}

`

23
задан Community 23 May 2017 в 12:30
поделиться

18 ответов

How about this approach? Purely cumulative - no back-tracking, and only iterates once. For raw performance, I'm not sure you'll do better with LINQ etc, regardless of how "pretty" a LINQ answer might be.

using System;
using System.Collections.Generic;
using System.Text;

static class Program
{
    public static string CommaQuibbling(IEnumerable<string> items)
    {
        StringBuilder sb = new StringBuilder('{');
        using (var iter = items.GetEnumerator())
        {
            if (iter.MoveNext())
            { // first item can be appended directly
                sb.Append(iter.Current);
                if (iter.MoveNext())
                { // more than one; only add each
                  // term when we know there is another
                    string lastItem = iter.Current;
                    while (iter.MoveNext())
                    { // middle term; use ", "
                        sb.Append(", ").Append(lastItem);
                        lastItem = iter.Current;
                    }
                    // add the final term; since we are on at least the
                    // second term, always use " and "
                    sb.Append(" and ").Append(lastItem);
                }
            }
        }
        return sb.Append('}').ToString();
    }
    static void Main()
    {
        Console.WriteLine(CommaQuibbling(new string[] { }));
        Console.WriteLine(CommaQuibbling(new string[] { "ABC" }));
        Console.WriteLine(CommaQuibbling(new string[] { "ABC", "DEF" }));
        Console.WriteLine(CommaQuibbling(new string[] {
             "ABC", "DEF", "G", "H" }));
    }
}
28
ответ дан 29 November 2019 в 00:40
поделиться
  1. НИКОГДА не используйте $ _ GET или $ _ POST без проверки и очистки.
  2. Прочтите о том, как настроить php.ini справа.
  3. Никогда не помещайте переменные в необработанный SQL.
  4. Если вы используете каркасы, используйте менее зависимые.
  5. Прекратите чрезмерное обобщение.
  6. Распределите код по файлы php. В большинстве случаев нет реальной необходимости помещать все в один index.php .
  7. Уменьшить сложность перед написанием кода.
  8. Учитывайте тот факт, что это веб-приложение. (Попробуйте быть RESTful .) Это не настольное приложение. Поэтому прекратите помещать все в $ _ SESSION .
  9. Как минимум одна строка комментария на каждые 10 строк кода. Вы прочитаете это через год. Обещаю!
1
ответ дан 29 November 2019 в 00:40
поделиться

Вы можете использовать foreach без LINQ, делегатов, замыканий, списков или массивов и при этом иметь понятный код. Используйте bool и строку, например, так:

public static string CommaQuibbling(IEnumerable items)
{
    StringBuilder sb = new StringBuilder("{");
    bool empty = true;
    string prev = null;
    foreach (string s in items)
    {
        if (prev!=null)
        {
            if (!empty) sb.Append(", ");
            else empty = false;
            sb.Append(prev);
        }
        prev = s;
    }
    if (prev!=null)
    {
        if (!empty) sb.Append(" and ");
        sb.Append(prev);
    }
    return sb.Append('}').ToString();
}
1
ответ дан 29 November 2019 в 00:40
поделиться

Я думаю, что Linq предоставляет довольно читаемый код. Эта версия обрабатывает миллион «ABC» за 0,89 секунды:

using System.Collections.Generic;
using System.Linq;

namespace CommaQuibbling
{
    internal class Translator
    {
        public string Translate(IEnumerable<string> items)
        {
            return "{" + Join(items) + "}";
        }

        private static string Join(IEnumerable<string> items)
        {
            var leadingItems = LeadingItemsFrom(items);
            var lastItem = LastItemFrom(items);

            return JoinLeading(leadingItems) + lastItem;
        }

        private static IEnumerable<string> LeadingItemsFrom(IEnumerable<string> items)
        {
            return items.Reverse().Skip(1).Reverse();
        }

        private static string LastItemFrom(IEnumerable<string> items)
        {
            return items.LastOrDefault();
        }

        private static string JoinLeading(IEnumerable<string> items)
        {
            if (items.Any() == false) return "";

            return string.Join(", ", items.ToArray()) + " and ";
        }
    }
}
1
ответ дан 29 November 2019 в 00:40
поделиться

Я не думаю, что использование старого доброго массива является ограничением. Вот моя версия с использованием массива и метода расширения:

public static string CommaQuibbling(IEnumerable<string> list)
{
    string[] array = list.ToArray();

    if (array.Length == 0) return string.Empty.PutCurlyBraces();
    if (array.Length == 1) return array[0].PutCurlyBraces();

    string allExceptLast = string.Join(", ", array, 0, array.Length - 1);
    string theLast = array[array.Length - 1];

    return string.Format("{0} and {1}", allExceptLast, theLast)
                 .PutCurlyBraces();
}

public static string PutCurlyBraces(this string str)
{
    return "{" + str + "}";
}

Я использую массив из-за метода string.Join и из-за возможности доступа к последнему элементу через индекс. Метод расширения здесь из-за DRY.

Я думаю, что снижение производительности происходит из списков list.ToArray () и string.Join , но я надеюсь, что все в одном этот кусок кода удобен для чтения и обслуживания.

1
ответ дан 29 November 2019 в 00:40
поделиться

Мне очень понравился ответ Джона, но это потому, что это очень похоже на то, как я подошел к проблеме. Вместо того, чтобы конкретно кодировать эти две переменные, я реализовал их в очереди FIFO.

Странно, потому что я предположил, что будет 15 постов, которые все делали одно и то же, но похоже, что мы были единственными двумя сделать это таким образом. Ох, глядя на эти ответы, ответ Марка Гравелла довольно близок к тому подходу, который мы использовали, но он использует два «цикла», а не держится за значения.

Но все эти ответы с помощью LINQ и regex и объединения массивов. просто кажется сумасшедшим! : -)

1
ответ дан 29 November 2019 в 00:40
поделиться

Вот мой, но я понимаю, что он очень похож на Марк, некоторые незначительные различия в порядке вещей, и я также добавил юнит-тесты.

using System;
using NUnit.Framework;
using NUnit.Framework.Extensions;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework.SyntaxHelpers;

namespace StringChallengeProject
{
    [TestFixture]
    public class StringChallenge
    {
        [RowTest]
        [Row(new String[] { }, "{}")]
        [Row(new[] { "ABC" }, "{ABC}")]
        [Row(new[] { "ABC", "DEF" }, "{ABC and DEF}")]
        [Row(new[] { "ABC", "DEF", "G", "H" }, "{ABC, DEF, G and H}")]
        public void Test(String[] input, String expectedOutput)
        {
            Assert.That(FormatString(input), Is.EqualTo(expectedOutput));
        }

        //codesnippet:93458590-3182-11de-8c30-0800200c9a66
        public static String FormatString(IEnumerable<String> input)
        {
            if (input == null)
                return "{}";

            using (var iterator = input.GetEnumerator())
            {
                // Guard-clause for empty source
                if (!iterator.MoveNext())
                    return "{}";

                // Take care of first value
                var output = new StringBuilder();
                output.Append('{').Append(iterator.Current);

                // Grab next
                if (iterator.MoveNext())
                {
                    // Grab the next value, but don't process it
                    // we don't know whether to use comma or "and"
                    // until we've grabbed the next after it as well
                    String nextValue = iterator.Current;
                    while (iterator.MoveNext())
                    {
                        output.Append(", ");
                        output.Append(nextValue);

                        nextValue = iterator.Current;
                    }

                    output.Append(" and ");
                    output.Append(nextValue);
                }


                output.Append('}');
                return output.ToString();
            }
        }
    }
}
1
ответ дан 29 November 2019 в 00:40
поделиться
public static string CommaQuibbling(IEnumerable<string> items)
{
  int count = items.Count();
  string answer = string.Empty;
  return "{" + 
      (count==0)  ?  ""  :  
         (  items[0] + 
             (count == 1 ? "" :  
                 items.Range(1,count-1).
                     Aggregate(answer, (s,a)=> s += ", " + a) +
                 items.Range(count-1,1).
                     Aggregate(answer, (s,a)=> s += " AND " + a) ))+ "}";
}

Он реализован как,

if count == 0 , then return empty,
if count == 1 , then return only element,
if count > 1 , then take two ranges, 
   first 2nd element to 2nd last element
   last element
1
ответ дан 29 November 2019 в 00:40
поделиться

Отказ от ответственности : я использовал это как предлог, чтобы поиграть с новыми технологиями, так что мои решения на самом деле не соответствуют Эрику (Я допускаю, что этот вариант foreach является превосходным, поскольку не требует ручного перебора с перечислителем.)

public static string NaiveConcatenate(IEnumerable<string> sequence)
{
    StringBuilder sb = new StringBuilder();
    sb.Append('{');

    IEnumerator<string> enumerator = sequence.GetEnumerator();

    if (enumerator.MoveNext())
    {
        string a = enumerator.Current;
        if (!enumerator.MoveNext())
        {
            sb.Append(a);
        }
        else
        {
            string b = enumerator.Current;
            while (enumerator.MoveNext())
            {
                sb.Append(a);
                sb.Append(", ");
                a = b;
                b = enumerator.Current;
            }
            sb.AppendFormat("{0} and {1}", a, b);
        }
    }

    sb.Append('}');
    return sb.ToString();
}

Решение с использованием LINQ

public static string ConcatenateWithLinq(IEnumerable<string> sequence)
{
    return (from item in sequence select item)
        .Aggregate(
        new {sb = new StringBuilder("{"), a = (string) null, b = (string) null},
        (s, x) =>
            {
                if (s.a != null)
                {
                    s.sb.Append(s.a);
                    s.sb.Append(", ");
                }
                return new {s.sb, a = s.b, b = x};
            },
        (s) =>
            {
                if (s.b != null)
                    if (s.a != null)
                        s.sb.AppendFormat("{0} and {1}", s.a, s.b);
                    else
                        s.sb.Append(s.b);
                s.sb.Append("}");
                return s.sb.ToString();
            });
}

Решение с TPL

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

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

public static string ConcatenateWithTpl(IEnumerable<string> sequence)
{
    var queue = new ConcurrentQueue<string>();
    bool stop = false;

    var consumer = Future.Create(
        () =>
            {
                var sb = new StringBuilder("{");
                while (!stop || queue.Count > 2)
                {
                    string s;
                    if (queue.Count > 2 && queue.TryDequeue(out s))
                        sb.AppendFormat("{0}, ", s);
                }
                return sb;
            });

    // Producer
    foreach (var item in sequence)
        queue.Enqueue(item);

    stop = true;
    StringBuilder result = consumer.Value;

    string a;
    string b;

    if (queue.TryDequeue(out a))
        if (queue.TryDequeue(out b))
            result.AppendFormat("{0} and {1}", a, b);
        else
            result.Append(a);

    result.Append("}");
    return result.ToString();
}

Для краткости модульные тесты были исключены.

2
ответ дан 29 November 2019 в 00:40
поделиться

Вот простое решение F #, которое выполняет только одну прямую итерацию:

let CommaQuibble items =
    let sb = System.Text.StringBuilder("{")
    // pp is 2 previous, p is previous
    let pp,p = items |> Seq.fold (fun (pp:string option,p) s -> 
        if pp <> None then
            sb.Append(pp.Value).Append(", ") |> ignore
        (p, Some(s))) (None,None)
    if pp <> None then
        sb.Append(pp.Value).Append(" and ") |> ignore
    if p <> None then
        sb.Append(p.Value) |> ignore
    sb.Append("}").ToString()

(РЕДАКТИРОВАТЬ: оказывается, это очень похоже на Skeet.)

Тестовый код:

let Test l =
    printfn "%s" (CommaQuibble l)

Test []
Test ["ABC"]        
Test ["ABC";"DEF"]        
Test ["ABC";"DEF";"G"]        
Test ["ABC";"DEF";"G";"H"]        
Test ["ABC";null;"G";"H"]        
2
ответ дан 29 November 2019 в 00:40
поделиться

Если бы я много делал с потоками, которые потребовалась бы первая / последняя информация, у меня было бы расширение thid:

[Flags]
public enum StreamPosition
{
   First = 1, Last = 2
}

public static IEnumerable<R> MapWithPositions<T, R> (this IEnumerable<T> stream, 
    Func<StreamPosition, T, R> map)
{
    using (var enumerator = stream.GetEnumerator ())
    {
        if (!enumerator.MoveNext ()) yield break ;

        var cur   = enumerator.Current   ;
        var flags = StreamPosition.First ;
        while (true)
        {
            if (!enumerator.MoveNext ()) flags |= StreamPosition.Last ;
            yield return map (flags, cur) ;
            if ((flags & StreamPosition.Last) != 0) yield break ;
            cur   = enumerator.Current ;
            flags = 0 ;
        }
    }
}

Тогда самое простое (не самое быстрое, которое потребовало бы еще несколько удобных методов расширения) будет:

public static string Quibble (IEnumerable<string> strings)
{
    return "{" + String.Join ("", strings.MapWithPositions ((pos, item) => (
       (pos &  StreamPosition.First) != 0      ? "" : 
        pos == StreamPosition.Last   ? " and " : ", ") + item)) + "}" ;
}
5
ответ дан 29 November 2019 в 00:40
поделиться

Неэффективно, но я думаю, что понятно.

public static string CommaQuibbling(IEnumerable<string> items)
{
    List<String> list = new List<String>(items);
    if (list.Count == 0) { return "{}"; }
    if (list.Count == 1) { return "{" + list[0] + "}"; }

    String[] initial = list.GetRange(0, list.Count - 1).ToArray();
    return "{" + String.Join(", ", initial) + " and " + list[list.Count - 1] + "}";
}

Если бы я поддерживал код, я бы предпочел это более умным версиям.

33
ответ дан 29 November 2019 в 00:40
поделиться

Как насчет пропуска сложного кода агрегации и просто очистки строки после ее построения?

public static string CommaQuibbling(IEnumerable<string> items)    
{
    var aggregate = items.Aggregate<string, StringBuilder>(
        new StringBuilder(), 
        (b,s) => b.AppendFormat(", {0}", s));
    var trimmed = Regex.Replace(aggregate.ToString(), "^, ", string.Empty);
    return string.Format(
               "{{{0}}}", 
               Regex.Replace(trimmed, 
                   ", (?<last>[^,]*)$", @" and ${last}"));
}

ОБНОВЛЕНО: Это не будет работать со строками с запятыми, как указано в комментариях. Я попробовал некоторые другие варианты, но без определенных правил относительно того, что могут содержать строки, у меня будут реальные проблемы с сопоставлением любого возможного последнего элемента с регулярным выражением, что делает это хорошим уроком для меня об их ограничениях.

1
ответ дан 29 November 2019 в 00:40
поделиться

Я фанат серийной запятой: я ем, стреляю и ухожу.

Мне постоянно нужно решение этой проблемы, и я решил ее на 3 языках (хотя не на C # ). Я бы адаптировал следующее решение (в Lua , не заключая в фигурные скобки ответ), написав метод concat , который работает с любым IEnumerable :

function commafy(t, andword)
  andword = andword or 'and'
  local n = #t -- number of elements in the numeration
  if n == 1 then
    return t[1]
  elseif n == 2 then
    return concat { t[1], ' ', andword, ' ', t[2] }
  else
    local last = t[n]
    t[n] = andword .. ' ' .. t[n]
    local answer = concat(t, ', ')
    t[n] = last
    return answer
  end
end
2
ответ дан 29 November 2019 в 00:40
поделиться

Это не очень хорошо читается, но хорошо масштабируется до десятков миллионов строк. Я разрабатываю на старой рабочей станции Pentium 4, и она выполняет 1000000 строк средней длины 8 примерно за 350 мс.

public static string CreateLippertString(IEnumerable<string> strings)
{
    char[] combinedString;
    char[] commaSeparator = new char[] { ',', ' ' };
    char[] andSeparator = new char[] { ' ', 'A', 'N', 'D', ' ' };

    int totalLength = 2;  //'{' and '}'
    int numEntries = 0;
    int currentEntry = 0;
    int currentPosition = 0;
    int secondToLast;
    int last;
    int commaLength= commaSeparator.Length;
    int andLength = andSeparator.Length;
    int cbComma = commaLength * sizeof(char);
    int cbAnd = andLength * sizeof(char);

    //calculate the sum of the lengths of the strings
    foreach (string s in strings)
    {
        totalLength += s.Length;
        ++numEntries;
    }

    //add to the total length the length of the constant characters
    if (numEntries >= 2)
        totalLength += 5;  // " AND "

    if (numEntries > 2)
        totalLength += (2 * (numEntries - 2)); // ", " between items

    //setup some meta-variables to help later
    secondToLast = numEntries - 2;
    last = numEntries - 1;

    //allocate the memory for the combined string
    combinedString = new char[totalLength];
    //set the first character to {
    combinedString[0] = '{';
    currentPosition = 1;

    if (numEntries > 0)
    {
        //now copy each string into its place
        foreach (string s in strings)
        {
            Buffer.BlockCopy(s.ToCharArray(), 0, combinedString, currentPosition * sizeof(char), s.Length * sizeof(char));
            currentPosition += s.Length;

            if (currentEntry == secondToLast)
            {
                Buffer.BlockCopy(andSeparator, 0, combinedString, currentPosition * sizeof(char), cbAnd);
                currentPosition += andLength;
            }
            else if (currentEntry == last)
            {
                combinedString[currentPosition] = '}'; //set the last character to '}'
                break;  //don't bother making that last call to the enumerator
            }
            else if (currentEntry < secondToLast)
            {
                Buffer.BlockCopy(commaSeparator, 0, combinedString, currentPosition * sizeof(char), cbComma);
                currentPosition += commaLength;
            }

            ++currentEntry;
        }
    }
    else
    {
        //set the last character to '}'
        combinedString[1] = '}';
    }

    return new string(combinedString);
}
2
ответ дан 29 November 2019 в 00:40
поделиться

Another variant - separating punctuation and iteration logic for the sake of code clarity. And still thinking about perfomrance.

Works as requested with pure IEnumerable/string/ and strings in the list cannot be null.

public static string Concat(IEnumerable<string> strings)
{
    return "{" + strings.reduce("", (acc, prev, cur, next) => 
               acc.Append(punctuation(prev, cur, next)).Append(cur)) + "}";
}
private static string punctuation(string prev, string cur, string next)
{
    if (null == prev || null == cur)
        return "";
    if (null == next)
        return " and ";
    return ", ";
}

private static string reduce(this IEnumerable<string> strings, 
    string acc, Func<StringBuilder, string, string, string, StringBuilder> func)
{
    if (null == strings) return "";

    var accumulatorBuilder = new StringBuilder(acc);
    string cur = null;
    string prev = null;
    foreach (var next in strings)
    {
        func(accumulatorBuilder, prev, cur, next);
        prev = cur;
        cur = next;
    }
    func(accumulatorBuilder, prev, cur, null);

    return accumulatorBuilder.ToString();
}

F# surely looks much better:

let rec reduce list =
    match list with
    | []          -> ""
    | head::curr::[]  -> head + " and " + curr
    | head::curr::tail  -> head + ", " + curr :: tail |> reduce
    | head::[] -> head

let concat list = "{" + (list |> reduce )  + "}"
2
ответ дан 29 November 2019 в 00:40
поделиться

Here as a Python one liner


>>> f=lambda s:"{%s}"%", ".join(s)[::-1].replace(',','dna ',1)[::-1]
>>> f([])
'{}'
>>> f(["ABC"])
'{ABC}'
>>> f(["ABC","DEF"])
'{ABC and DEF}'
>>> f(["ABC","DEF","G","H"])
'{ABC, DEF, G and H}'

This version might be easier to understand


>>> f=lambda s:"{%s}"%" and ".join(s).replace(' and',',',len(s)-2)
>>> f([])
'{}'
>>> f(["ABC"])
'{ABC}'
>>> f(["ABC","DEF"])
'{ABC and DEF}'
>>> f(["ABC","DEF","G","H"])
'{ABC, DEF, G and H}'
3
ответ дан 29 November 2019 в 00:40
поделиться

Поздняя запись:

public static string CommaQuibbling(IEnumerable<string> items)
{
    string[] parts = items.ToArray();
    StringBuilder result = new StringBuilder('{');
    for (int i = 0; i < parts.Length; i++)
    {
        if (i > 0)
            result.Append(i == parts.Length - 1 ? " and " : ", ");
        result.Append(parts[i]);
    }
    return result.Append('}').ToString();
}
2
ответ дан 29 November 2019 в 00:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: