检查C#中字符串列表中是否存在字符串

时间:2016-10-28 06:46:58

标签: c# string linq

您好我正在尝试找到最好和最有效的方法来预先形成字符串列表中的字符串。

我的情况是这样的:

  1. 从数据库中获取满足给定条件的所有字符串值。
  2. 创建具有6位数的随机代码,但它是字符串值。
  3. 检查字符串列表中是否存在生成的代码。如果它确实再次生成代码,并执行它,直到找到字符串列表中不存在的唯一字符串。如果找到,请将其退回。
  4. 这是我的代码:

    private static readonly string chars = "0123456789";
    
    string IGenerateOtpCodeService.GenerateOtpCode()
    {
        var otps = personalTestSessionRepository
            .FindAll(x => x.State == PersonalTestSessionStates.NotStarted)
            .Select(x => x.Person.Otp)
            .ToList()
            .Distinct();
    
        Random random = new Random();
    
        string otp = new string(Enumerable.Repeat(chars, 6)
            .Select(s => s[random.Next(s.Length)]).ToArray());
    
        //preform check if otp exitst in otps list. if it does, generate otp again, else return otp
        return otp;
    } 
    

    最好的方法是什么?是while while循环,一些LINQ表达还是别的什么?

6 个答案:

答案 0 :(得分:3)

您应该在Distinct之前使用ToList在数据库服务器上执行Distinct操作。然后,您可以使用Any检查字符串是否存在。

var otps = personalTestSessionRepository
    .FindAll(x => x.State == PersonalTestSessionStates.NotStarted)
    .Select(x => x.Person.Otp)
    .Distinct();
    .ToList();

string otp = null;
var found = true; 
do
{
    otp = new string(Enumerable.Repeat(chars, 6)
    .Select(s => s[random.Next(s.Length)]).ToArray());

    found = otps.Any(x=>x == otp);

} while(found)

return otp;

根据heinzbeinz的建议,如果您使用HashSet,代码的执行速度会更快。要使用HashSet,请使用以下代码

var otpsList = personalTestSessionRepository
    .FindAll(x => x.State == PersonalTestSessionStates.NotStarted)
    .Select(x => x.Person.Otp)
    .Distinct();
    .ToList();

var otps = new HashSet<string>(otpsList);

string otp = null;
var found = true; 
do
{
    otp = new string(Enumerable.Repeat(chars, 6)
    .Select(s => s[random.Next(s.Length)]).ToArray());

    found = otps.Contains(otp);

} while(found)

return otp;

答案 1 :(得分:2)

为什么不简单地反转逻辑并获取数据库中已经存在的所有数字,而不是一次又一次地查找直到?对它们进行排序并逐个递增最大值以获得不存在的数字:

var ordered = personalTestSessionRepository
    .FindAll(x => x.State == PersonalTestSessionStates.NotStarted)
    .Select(x => x.Person.Otp)
    .Order(x => x);

var newNumber = ordered.Last().Otp + 1;

或者Otp是首先转换为数字的字符串:

var newNumber = (Convert.ToInt32(ordered.Last().Otp) + 1).ToString();

当几乎所有数字都在使用时,这种方法可能会更快,因此猜测任意数字可能需要很长时间,因为您可能会多次猜测相同的数字。

编辑:另外,您可以简单地使用Max(x => Convert.ToInt32(x.Otp)) + 1

答案 2 :(得分:2)

首先,在List的情况下,O(N)中的搜索时间复杂度为HashSet只是O(1)

 var ops = new HashSet<int>(personalTestSessionRepository
   .FindAll(x => x.State == PersonalTestSessionStates.NotStarted)
   .Select(x => x.Person.Otp));

接下来,将Random放在方法上(或者可能会使它严重偏斜)。 让Random作为integer使用,然后将其转换为String

 // Simplest, but not thread safe
 private static Random random = new Random();

 string IGenerateOtpCodeService.GenerateOtpCode() {
   var ops = new HashSet<int>(personalTestSessionRepository
     .FindAll(x => x.State == PersonalTestSessionStates.NotStarted)
     .Select(x => x.Person.Otp));

   int value = -1;

   while (true) {
     value = random.Next(10000000);

     if (!ops.Contains(value)) 
       return value.ToString();
   }
 }  

如果你想要任何数字,不一定是随机,你可以寻找一个第一洞

string IGenerateOtpCodeService.GenerateOtpCode() {
   var ops = personalTestSessionRepository
     .FindAll(x => x.State == PersonalTestSessionStates.NotStarted)
     .Select(x => x.Person.Otp)
     .OrderBy(x => x);

   bool first = true;
   int prior = -1;

   foreach (var item in ops) {
     if (!first && item != prior + 1)
       return item.ToString();

     first = false;
     prior = item; 
   }    

   // no holes, we might want to return the last item + 1
   return (prior + 1).ToString();
}

答案 3 :(得分:0)

那么,根据数据库中字符串的数量,最好的选择可能是在db中找到它,而不是将所有数据加载到客户端并在之后搜索字符串。是的,这可能更容易编写,但如果你有很多字符串,那么你应该在数据库中查询它。当然,你应该有适当的指数来确保它的速度很快。

答案 4 :(得分:0)

添加while循环

    while (true)
    {
        string otp = new string(Enumerable.Repeat(chars, 6)
        .Select(s => s[random.Next(s.Length)]).ToArray());

        if (otps.Contains(otp))
        {
            return otp;
        }
    }

答案 5 :(得分:0)

一种方法是对数据库中的列表进行排序。 当您有排序列表时,您可以简单地使用二进制搜索来检查字符串是否存在。