簡體   English   中英

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

[英]Check if string exists in list of strings in C#

您好我正在嘗試找到最好和最有效的方法來預先形成字符串列表中的字符串。

我的情況是這樣的:

  1. 從數據庫中獲取滿足給定條件的所有字符串值。
  2. 創建具有6位數的隨機代碼,但它是字符串值。
  3. 檢查生成的代碼是否存在於字符串列表中。 如果它確實再次生成代碼,並執行它,直到找到字符串列表中不存在的唯一字符串。 當你找到一個,返回它。

這是我的代碼:

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循環,一些LINQ表達,還是其他什么?

您應該在ToList之前使用Distinct在數據庫服務器上執行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;

而不是一次又一次地找,直到一個值不存在,為什么不能簡單地恢復邏輯,並采取所有已經存在於數據庫的數字? 對它們進行排序並逐個遞增最大值以獲得不存在的數字:

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

首先,在HashSet情況下,在List搜索具有O(N)時間復雜度只是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();
}

那么,根據數據庫中字符串的數量,最好的選擇可能是在db中找到它,而不是將所有數據加載到客戶端並在之后搜索字符串。 是的,這可能更容易編寫,但如果你有很多字符串,那么你應該在數據庫中查詢它。 當然你應該有適當的索引來確保它的速度很快。

添加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;
        }
    }

一種方法是對數據庫中的列表進行排序。 當您有排序列表時,您可以簡單地使用二進制搜索來檢查字符串是否存在。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM