Строка в словарь с использованием регулярных выражений (хотите оптимизировать) - PullRequest
3 голосов
/ 16 апреля 2011

У меня есть строка в формате "$0Option one$1$Option two$2$Option three" (и т. Д.), Которую я хочу преобразовать в словарь, где каждое число соответствует опции. В настоящее время у меня есть рабочее решение этой проблемы, но поскольку этот метод вызывается для каждой импортируемой записи (несколько тысяч), я хочу, чтобы она была максимально оптимизирована.

public Dictionary<string, int> GetSelValsDictBySelValsString(string selectableValuesString)
{
    // Get all numbers in the string. 
    var correspondingNumbersArray = Regex.Split(selectableValuesString, @"[^\d]+").Where(x => (!String.IsNullOrWhiteSpace(x))).ToArray();

    List<int> correspondingNumbers = new List<int>();

    int number;
    foreach (string s in correspondingNumbersArray)
    {
        Int32.TryParse(s, out number);
        correspondingNumbers.Add(number);
    }

    selectableValuesString = selectableValuesString.Replace("$", "");

    var selectableStringValuesArray = Regex.Split(selectableValuesString, @"[\d]+").Where(x => (!String.IsNullOrWhiteSpace(x))).ToArray();

    var selectableValues = new Dictionary<string, int>();

    for (int i = 0; i < selectableStringValuesArray.Count(); i++)
    {
        selectableValues.Add(selectableStringValuesArray.ElementAt(i), correspondingNumbers.ElementAt(i));
    }

    return selectableValues;
}

Ответы [ 2 ]

5 голосов
/ 17 апреля 2011

Первое, что привлекло мое внимание в вашем коде, это то, что он обрабатывает входную строку три раза: дважды с Split() и один раз с Replace(). Для этой работы метод Matches() гораздо лучше, чем Split(). С его помощью вы можете извлечь все необходимое за один проход. Это также облегчает чтение кода.

Вторым, что я заметил, были все эти петли и промежуточные объекты. Вы уже используете LINQ; действительно используйте его, и вы можете устранить все эти помехи и повысить производительность. Проверьте это:

public static Dictionary<int, string> GetSelectValuesDictionary(string inputString)
{
  return Regex.Matches(inputString, @"(?<key>[0-9]+)\$*(?<value>[^$]+)")
    .Cast<Match>()
    .ToDictionary(
        m => int.Parse(m.Groups["key"].Value),
        m => m.Groups["value"].Value);
}

примечания:

  • Cast<Match>() необходимо, потому что MatchCollection объявляет себя только как IEnumerable, и нам нужно, чтобы оно было IEnumerable<Match>.
  • Я использовал [0-9] вместо \d на случай, если ваши значения могут содержать цифры из нелатинских систем записи; в .NET \d соответствует им всем.
  • Статические методы Regex, такие как Matches(), автоматически кэшируют объекты Regex, но если этот метод будет вызываться много (особенно если вы используете много других регулярных выражений, тоже), вы можете создать статический Regex объект в любом случае. Если производительность действительно критична, вы можете указать опцию Compiled, пока она у вас.
  • Мой код, как и ваш, не пытается справиться с некорректным вводом. В частности, мое сгенерирует исключение, если число окажется слишком большим, а ваше просто преобразует его в ноль. Возможно, это не относится к вашему реальному коду, но я был вынужден выразить свое беспокойство, увидев, что вы звоните TryParse() без проверки возвращаемого значения. : /
  • Вы также не уверены, что ваши ключи уникальны. Как и @Gabe, я перевернул его, используя числовые значения в качестве ключей, потому что они оказались уникальными, а строковые значения - нет. Я верю, что это тоже не проблема с вашими реальными данными. ;)
2 голосов
/ 16 апреля 2011

Ваш selectableStringValuesArray на самом деле не массив! Это означает, что каждый раз, когда вы индексируете его (с помощью ElementAt или считаете его с помощью Count), он должен повторно запускать регулярное выражение и проходить по списку результатов в поисках непробельных пробелов. Вам нужно что-то вроде этого:

var selectableStringValuesArray = Regex.Split(selectableValuesString, @"[\d]+").Where(x => (!String.IsNullOrWhiteSpace(x))).ToArray();

Вы также должны исправить свой correspondingNumbersString, потому что у него та же проблема.

Я вижу, что вы используете C # 4, поэтому вы можете использовать Zip для объединения списков, и тогда вам не нужно будет создавать массив или использовать какие-либо циклы. Вы можете создать свой словарь так:

return correspondingNumbersString.Zip(selectableStringValuesArray,
       (number, str) => new KeyValuePair<int, string>(int.Parse(number), str))
      .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...