Ищем более простой способ преобразования строки с разделителями в StringDictionary

#c#

#c#

Вопрос:

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

 private const string STREETTYPES = @"ALY|Alley|AVE|Avenue|BLVD|Boulevard|CIR|Circle|CT|Court|CTR|Center|DR|Drive|EXPY|Expressway|FWY|Freeway|HALL|Hall|HWY|Highway|JCT|Junction|LN|Lane|LP|Loop|PIKE|Pike|PKWY|Parkway|PL|Place|RD|Road|ST|Street|TER|Terrace|TPKE|Turnpike|TRL|Trail|WAY|Way";

  private static StringDictionary streetTypes = null;
  public static StringDictionary StreetTypes
  {
    get
    {
      if (streetTypes != null) return streetTypes;
      streetTypes = new StringDictionary();
      var streetArray = STREETTYPES.Split(PIPE);
      for (int i = 0; i < streetArray.Length-1; i = i 2)
      {
        streetTypes.Add(streetArray[i], streetArray[i   1]);
      }
      return streetTypes;
    }
  }
  

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

1. является ли строка на самом деле константой в реализации? Я думаю, что инициализатор коллекции для словаря был бы довольно аккуратным…

2. Согласен. Определенно лучший вариант.

Ответ №1:

Как насчет просто:

 private static readonly StringDictionary streetTypes = new StringDictionary
{
    {"ALY","Alley"},{"AVE","Avenue"},{"ALY","Alley"},{"BLVD","Boulevard"},{"CIR","Circle"},
    {"CT","Court"},{"CTR","Center"},{"DR","Drive"},{"EXPY","Expressway"},{"FWY","Freeway"},
    {"HALL","Hall"},{"HWY","Highway"},{"JCT","Junction"},{"LN","Lane"},{"LP","Loop"},
    ...        
};
  

Ответ №2:

Вы можете использовать Linq, если вам нужно пересчитать, но если словарь статичен, просто используйте инициализатор:

 var input = STREETTYPES.Split('|');
var dict = input.Select( (x,i) => new { Item = x, Index = i })
                .Where(x => x.Index % 2 == 0)
                .ToDictionary( x=> input[x.Index], x => input[x.Index   1]);
  

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

1. Это не создает StringDictionary.

2. @neontapir: Согласен — возможно, хотя, если OP хочет использовать Linq, он должен использовать a Dictionary<string,string> в любом случае

Ответ №3:

Это хорошо?

 var x = STREETTYPES.Split(new[] {'|'});
var output = Enumerable
    .Range(0, x.Length / 2)
    .ToDictionary(s => x[2 * s], s => x[2 * s   1]);
  

Возможно, можно было бы ее еще больше сжать, но сегодня я наполовину сплю.

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

1. Это сработало бы, если бы диапазон содержал x.Length / 2 элементы и выбранные индексы 2*x и 2*x 1 .

2. Что он и делает (удобно). Если этого не произойдет, он, вероятно, должен сначала с треском умереть с утверждением.

3. Это не сработает, это создаст n-1 элементов ({0,1}, {1,2}, {2,3} …), и то, чего хочет ОП, — это n/2 ({0,1}, {2,3}, {4,5}…).

4. Теперь это имеет смысл — быстро исправит код. Сказал, что я наполовину спал 🙂

5. Исправлено! Спасибо за предупреждение, все оценили. Обычно модульный тест обнаружил бы это (плохое оправдание ;-))

Ответ №4:

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

 var types = new StringDictionary();
using (IEnumerator<string> enumerator = streetTypes.Split('|').AsEnumerable().GetEnumerator())
{
    while(enumerator.MoveNext())
    {
        string first = enumerator.Current;
        if (!enumerator.MoveNext()) 
            break;
        types.Add(first, enumerator.Current);
    }
}
  

Ответ №5:

Бесстыдно срывая конструктор Python dict:

 IDictionary<TKey, TVal> 
ToDictionary<TKey, TVal>(IEnumerable<TKey> keys, 
                         IEnumerable<TVal> values) 
{
    return keys.Zip(values, (k, v)=>new {K=k, V=v}).ToDictionary(kv=>kv.K, kv=>kv.V);
}

var str = @"ALY|Alley|AVE|Avenue|BLVD|Boulevard|CIR|Circle|CT|Court|CTR|Center|DR|Drive|EXPY|Expressway|FWY|Freeway|HALL|Hall|HWY|Highway|JCT|Junction|LN|Lane|LP|Loop|PIKE|Pike|PKWY|Parkway|PL|Place|RD|Road|ST|Street|TER|Terrace|TPKE|Turnpike|TRL|Trail|WAY|Way";
var words = str.Split('|');
var keys = words.Where((w, i) => i%2 == 0);
var values = words.Where((w, i) => i%2 != 0);
var dict = ToDictionary(keys, values);
  

Это приведет к a Dictionary<string, string> , а не a StringDictionary . У последнего нет конструкторов копирования, поэтому его нельзя создать с помощью oneliner .

(Теперь я хочу, чтобы в C # было меньше элементарной поддержки специальных структур данных, я скучаю по деструктурирующим назначениям.)

Альтернативная версия, которая не требует Zip() и, таким образом, будет работать в более старых версиях .NET и позволяет избежать выделения списка временных пар ключ / значение:

 IDictionary<TKey, TVal> 
ToDictionary<TKey, TVal>(IEnumerable<TKey> keys, 
                         IEnumerable<TVal> values) 
{
    return Enumerable.Range(0, keys.Count()).ToDictionary(i=>keys.ElementAt(i), i=>values.ElementAt(i));
}