在一个 LINQ 语句中选择值并附加数字?
Posted
技术标签:
【中文标题】在一个 LINQ 语句中选择值并附加数字?【英文标题】:Select value and append number to it in one LINQ statement? 【发布时间】:2013-05-13 15:23:39 【问题描述】:我的数据库中有以下两个名称:SMITH
和 SMITH1
如果数据库中存在名称,如果只有 SMITH
存在,我想给它附加一个 1,并且如果带有数字的名称已经存在,我想增加名称的数字部分。
是否可以在一个 LINQ 语句中执行此操作?现在,我正在做:
string name = string.Format("012",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);
由于我的 while 循环中有 LINQ 查询,它是否会在每次必须循环返回时执行选择?
另外,如果有人知道一种方法可以使它更简洁但可读,那会很棒吗?
这确实适用于没有数字或 1 - 9 的名称,但是当我到达 10 时会发生什么,它只会替换最后一个字符,在这种情况下为 0,我该如何处理?
有没有办法在 LINQ 中获取数字部分,所以 SMITH12
会返回 12
【问题讨论】:
@TimSchmelter -while(names.SingleOrDefault (d => d.Name== name) != null)
。这不是 LINQ 查询吗?
@TimSchmelter:他的 while 循环中有一个 LINQ 语法语句作为他的 if
条件。
@TimSchmelter - 它的正确名称是什么?
我认为这是一个 LINQ 查询,因为我认为它会像 SELECT Name FROM Table WHERE NAME = 'xxxxx'
这样对数据库进行查询。
我支持@Jeff。您的方法需要大量迭代,而检查用户名并建议替代方案(通过适当的可用性检查)是一种更简单的方法。它还可以防止您的系统泄露与注册的名为“JohnDoe”的用户数量相关的信息。
【参考方案1】:
您的代码似乎不正确,无法编译。
这应该可行:
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”,因为它是第二个名称。
【讨论】:
我遗漏了一些部分,但它确实可以编译和运行。我自己做过。我会试试你的解决方案。 您的解决方案有效,但我有几个问题:1)这会访问数据库多少次? 2) n.Name.Last() 是否只检查最后一个字符,如果我想检查最后哪些字符是数字怎么办?这需要正则表达式吗? 如果永远不会有重复,OrderByDescending 是否必要? @Xaisoft:你在唱什么 linq-provider,Linq-To-Sql?我很惊讶它完全适用于所有 text 和 char 方法,我忽略了您正在使用数据库。根查询检查是否有重复项。lastNameWithNumber
采用具有最高数字结尾的副本,因为 OrderByDescending
它适用于零个、一个或多个数字副本。
我正在使用 Linqpad 对内存中的集合进行尝试,但是当我针对我的实际数据库进行尝试时,我得到了错误:LINQ to Entities does not recognize the method 'Boolean IsDigit(Char)' method, and this method cannot be translated into a store expression.
【参考方案2】:
使用类似的东西。
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
中枚举一次。我建议使用 existingNames
的 HashSet 而不是 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();
【讨论】:
【参考方案3】:这将检索下一个名称(例如,如果最近的增量是 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
【讨论】:
【参考方案4】:你应该做些类似的事情:
遍历您的可枚举名称列表。对于每个这样的名称,
将其拆分为其组成部分(基本名称和可选的数字后缀) 扔掉任何基本名称与所需名称不匹配的项目 找出剩余集合的最高后缀值(可能没有)。如果您找到了最大后缀,请将其加 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() : "" ) ;
【讨论】:
【参考方案5】:这是一种非常幼稚但易于阅读的方式来做你想做的事情:
//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
有很多方法可以提高效率,其中一些会牺牲可读性。
如果需要,我会将这段代码的优化留给你作为练习。
【讨论】:
我试过这个。如果我已经有类似 JSMITH 的东西,它会返回 JSMITH,而不是 JSMITH1 对不起,我看错了你的评论,然后在上面的代码中找到了一个错误。 bool 的初始设置不正确 - 仅当存在欺骗时才设置为 'true'(说名称很好)。对不起。以上是关于在一个 LINQ 语句中选择值并附加数字?的主要内容,如果未能解决你的问题,请参考以下文章