2013-05-13 3 views
4

У меня есть следующие два имени в моей базе данных: SMITH и SMITH1Выберите значение и добавьте его в один оператор LINQ?

Если имя выходит в базе данных, я хочу либо присоединять к нему 1, если только SMITH выходов и, если имя с номером уже выходит, Я хочу увеличить числовую часть имени.

Возможно ли это сделать в одном статусе LINQ? Прямо сейчас, я делаю:

string name = string.Format("{0}{1}{2}",first_name,middle_name,last_name).ToUpper(); 

//Check for duplicates 
while(names.SingleOrDefault (d => d.Name== name) != null) 
{ 
    //Get last char 
    char lastChar = name[name.Length - 1]; 

    //Last char is number 
    if(Char.IsNumber(lastChar)) 
    { 
     name = name.Replace(lastChar,Convert.ToChar(Convert.ToInt32(lastChar) + 1)); 
    } 
    else 
    { 
     name = (name + 1); 
    } 
} 

Поскольку у меня есть запрос LINQ в мое время цикла, он будет делать выбор каждый раз, когда он имеет к циклу назад вокруг?

Кроме того, если кто-нибудь знает способ сделать это более кратким, но читаемым, это было бы здорово?

Это работает для имен, которые не имеют числа или 1 - 9, но что произойдет, когда я доберусь до 10, он заменит только последний символ, в данном случае 0, как мне это сделать?

Есть ли способ, чтобы получить числовую часть в LINQ, так SMITH12 вернется 12

+0

@TimSchmelter - 'в то время как (names.SingleOrDefault (d => d.Name == имя)! = NULL)'. Разве это не запрос LINQ? – Xaisoft

+0

@TimSchmelter: У него есть оператор LINQ-синтаксиса как его условие 'if' в его цикле while. – Jeff

+0

@TimSchmelter - Что он правильно назвал? – Xaisoft

ответ

0

Вот очень наивным, но легко читаемый способ сделать то, что вы хотите сделать:

//Check for duplicates 
bool nameValid = names.SingleOrDefault (d => d.Name == name) == null; 
int count = 1; 
while(!nameValid) 
{ 
    if(names.SingleOrDefault (d => d.Name == name + count.ToString()) == null) 
    { 
     name = name + count.ToString(); 
     nameValid = true; 
    } 
    else 
    { 
     count++; 
    } 
} 
// name now contains a unique name 

Существует несколько способов сделать это более эффективным, а некоторые - за счет удобочитаемости.

Я останусь оптимизацией этого кода в качестве упражнения для вас, если он вам понадобится.

+0

Я пробовал это. Если у меня уже есть что-то вроде JSMITH, он возвращает JSMITH, а не JSMITH1 – Xaisoft

+0

Извините, я неправильно прочитал ваш комментарий, а затем обнаружил ошибку в приведенном выше коде. – Jeff

+0

Исходная настройка bool была неправильной - для нее было установлено значение «true» (если имя было хорошим), только если был обман. Сожалею. – Jeff

0

Ваш код не соответствует действительности и не компилируется.

Это должно работать:

var sameNames = names 
    .Where(n => n.Name.Length >= name.Length && n.Name.Substring(0,name.Length)==name); 
if (sameNames.Any()) 
{ 
    var lastNameWithNumber = sameNames 
     .Where(n => Char.IsDigit(n.Name.Last())) 
     .Select(n => new { 
      n.Name, 
      Num = int.Parse(new string(n.Name.Reverse().TakeWhile(Char.IsDigit).Reverse().ToArray())) 
     }) 
     .OrderByDescending(x => x.Num) 
     .FirstOrDefault(); 
    if (lastNameWithNumber != null) 
     name = name + (lastNameWithNumber.Num + 1); 
    else 
     name = name + "2"; 
} 

Проверяется, если один или несколько объектов Name начать с данным именем. Если это так, то первый запрос linq проверяет, есть ли дубликаты с номерами в конце. Затем он будет заказывать по этому номеру desc и выбирает первый (с наибольшим числом). Новое имя будет иметь имя + высший номер + 1. В противном случае он просто будет использовать имя + «2», так как это второе имя.

+0

Я пропустил некоторые части, но он компилируется и запускается. Я сделал это сам. Я попробую ваше решение. – Xaisoft

+0

Ваше решение работает, но у меня есть пара вопросов: 1) Сколько раз это попадало в базу данных? 2) Проверяет ли n.Name.Last() только последний символ, что, если я хочу проверить, какие символы в конце являются числами? Для этого требуется регулярное выражение? – Xaisoft

+0

Если дубликатов не будет, требуется ли OrderByDescending? – Xaisoft

0

Используйте что-то вроде этого.

IEnumerable<string> existingNames = ...; 

string rawName = ...; 

string finalName = (from suffix in Enumerable.Range(0, int.MaxValue) 
        let testName = rawName + (suffix == 0 ? "" : suffix.ToString()) 
        where existingNames.All(n => n != testName) 
        select testName).FirstOrDefault(); 

отмечает, что existingNames перечисляется один раз в suffix. Я бы рекомендовал использовать HashSet для existingNames вместо IEnumerable<string>.

HashSet<string> existingNames = ...; 

string rawName = ...; 

string finalName = (from suffix in Enumerable.Range(0, int.MaxValue) 
        let testName = rawName + (suffix == 0 ? "" : suffix.ToString()) 
        where !existingNames.Contains(testName) 
        select testName).FirstOrDefault(); 
0

Это будет получить следующее имя (например, если последнее приращение NAME10, следующее имя будет NAME11):

int tmp; 
var existingNames = from name in names 
       let numericIndex = name.IndexOfAny(new char[] {'1','2','3','4','5','6','7','8','9','0'}, 0) 
       let subName = numericIndex > -1 ? name.Substring(0, numericIndex) : name 
       let IndexAsString = numericIndex > -1 ? name.Substring(numericIndex) : "0" 
       let index = int.TryParse(IndexAsString, out tmp) ? tmp : 0 
       group new { subName, index } by subName into gs 
       select gs; 
var newNames = from existingName in existingNames 
       select new { BaseName = existingName.Key, Index = existingName.Max(a => a.index) + 1 }; 
if (newNames.Any(a => a.BaseName == nameToAdd)) { 
    // do your work to change the name 
} 
0

Что-то вдоль этих линий должны сделать вам:

Итерации над перечислимым списком имен.Для каждого такого имени,

  • разделить его на составные компоненты (а базовое имя и необязательный числовой суффикс)
  • бросить все детали, где имя базы не соответствует нужное имя
  • найти самый высокий значение суффикса оставшегося набора (которое может быть ничем).

Если вы нашли максимальный суффикс, добавьте 1 к нему и добавьте его к нужному имени, в противном случае верните желаемое имя как есть.

Пример:

private static readonly Regex rxNameSuffix = new Regex(@"^(?<name>.+)(?<suffix>\d*)$") ; 
string GenerateName(string name , IEnumerable<string> existingNames) 
{ 
    if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("name") ; 
    int? maxSuffix = existingNames.Select(s => { 
      Match m = rxNameSuffix.Match(s) ; 
      if (!m.Success) throw new InvalidOperationException("that shouldn't have happened") ; 
      string pfx = m.Groups["name"].Value ; 
      int? sfx = m.Groups["suffix"].Value.Length == 0 ? (int?)null : int.Parse(m.Groups["suffix"].Value) ; 
      return new Tuple<string,int?>(pfx , sfx) ; 
     }) 
     .Where(x => name.Equals(x.Item1 , StringComparison.OrdinalIgnoreCase)) 
     .Aggregate((int?)null , (acc,x) => { 
      int? newacc ; 
      if (!x.Item2.HasValue) return acc ; 
      return !acc.HasValue || acc.Value < x.Item2.Value ? x.Item2 : acc ; 
     }) ; 
    int? suffix = maxSuffix.HasValue ? maxSuffix+1 : null ; 
    return name + (suffix.HasValue ? suffix.ToString() : "") ; 
} 
Смежные вопросы