Как с помощью linq отсеять элементы в двух массивах?
Есть 2 строковых массива с большим количеством строк.
string[] words1;
string[] words2;
Если в этих двух массивах находится совпадение по заданному мной условию, нужно оба элемента добавить в разные листы/массивы. Примерный код:
List<string> russian = new List<string>();
sr = new StreamReader(@"russian.txt");//1 строка=1 слово русского языка
while(!sr.EndOfStream)
{
russian.Add(sr.ReadLine());
}//примерно 1.5 миллиона строк
int[] qw = { -1 };//индексы, которые надо игнорировать, сейчас не используется
var a = russian.Where(q => q.Length == 8);//отсеиваем все слова с 8 буквами
var b = a.Where(q => Check(q, qw));//отсеиваем слова с повторяющимися буквами
var a1 = russian.Where(q => q.Length == 7);//отсеиваем все слова с 7 буквами
var b1 = a1.Where(q => Check(q, qw));//отсеиваем слова с повторяющимися буквами
string[] word1 = b.ToArray();//46к строк
string[] word2 = b1.ToArray();//43к строк
List<string> word11 = new List<string>();
List<string> word22 = new List<string>();
for(int i=0;i<word1.Length;i++)
{
for(int j=i+1; j<word2.Length;j++)
{
if(word1[i][0]==word2[j][0]&& word1[i][4] == word2[j][2] && word1[i][7] == word2[j][6])
{
if (!word11.Exists(q => q.Equals(word1[i])))
word11.Add(word1[i]);
if (!word22.Exists(q => q.Equals(word2[j])))
word22.Add(word2[j]);
}
Console.WriteLine(i + " " + j);
}
}
static bool Check(string str,int[] ind)//проверяет, имеет ли строка повторяющиеся символы, int[] ind - игнорируемые индексы
{
bool q=true;
string a = "";
int t=0;
for(int i=0;i<str.Length;i++)
{
if (i != ind[t])
a += str[i];
else
{
i++;t++;
}
}
for (int i = 0; i < a.Length; i++)
{
for (int j=i+1;j<a.Length;j++)
{
if (a[i] == a[j])
q = false;
}
}
return q;
}
Но в лучшем случае он будет выполняться целые сутки.
Как с помощью linq выполнить этот же код?
UPD:
Вот пример попроще:
string[] a1 = { "apple", "nilmar", "anpsa" };
string[] a2 = { "ars", "aep", "fff", "arp" };
Нужно из массивов вытянуть те элементы, у которых первый и третий символы совпадают, т.е. в идеале должно быть два листа
List<string> word1 = new List<string>();
List<string> word2 = new List<string>();
Где в первом будут лежать "apple" и "anpsa", а во втором "aep" и "arp"
Ответы (1 шт):
Попробовал немного оптимизировать
static void Main(string[] args)
{
int[] qw = Array.Empty<int>();
List<string> words1 = new List<string>();
List<string> words2 = new List<string>();
using (var sr = new StreamReader("russian.txt"))
{
while (!sr.EndOfStream)
{
string word = sr.ReadLine();
if (word.Length == 8 && Check(word, qw))
words1.Add(word);
else if (word.Length == 7 && Check(word, qw))
words2.Add(word);
}
}
// для контроля уникальности используем специально оптимизированные для этого коллекции
HashSet<string> result1 = new HashSet<string>();
HashSet<string> result2 = new HashSet<string>();
for (int i = 0; i < words1.Count; i++)
{
string w1 = words1[i]; // кешируем ссылку на строку, чтобы не обращаться много раз через индекс
for (int j = i + 1; j < words2.Count; j++)
{
string w2 = words2[j];
if (w1[0] == w2[0] && w1[4] == w2[2] && w1[7] == w2[6])
{
result1.Add(w1);
result2.Add(w2);
}
}
}
File.WriteAllLines("words1.txt", result1);
File.WriteAllLines("words2.txt", result2);
Console.ReadKey();
}
// индексы должны быть уникальны и отсортированы по возрастанию
// если нет индеков для исключения, массив индексов должен быть пустым
static bool Check(string str, int[] indexes)
{
string word = str.ToLower();
if (indexes.Length == 0)
return word.Distinct().Count() == word.Length;
HashSet<char> distinct = new HashSet<char>();
int i = 0;
while (true)
{
while (Array.BinarySearch(indexes, i) >= 0)
i++;
if (i == word.Length)
break;
if (!distinct.Add(word[i++]))
return false.
}
return true;
}
Если у вас так быстро выполняется, не могли бы, пожалуйста, добавить третье слово из 6 букв с условием
w1[0] == w2[0] && w1[4] == w2[2] && w1[7] == w2[6]&& w3[1] == w2[1] && w3[3] == w1[3] && w3[4] == w2[2] && w3[4] == w1[4]и скинуть результаты?
3 уровня вложенности - это очень долго, я бы сказал - кубическая сложность здесь будет выполняться вечность, но вот, я немного распараллелил код на уровне внешнего цикла, он кушает весь процессор полностью.
static async Task Main(string[] args)
{
int[] qw = Array.Empty<int>();
List<string> words1 = new List<string>();
List<string> words2 = new List<string>();
List<string> words3 = new List<string>();
using (var sr1 = new StreamReader("russian.txt"))
{
while (!sr1.EndOfStream)
{
string word = sr1.ReadLine();
if (word.Length == 8 && Check(word, qw))
words1.Add(word);
else if (word.Length == 7 && Check(word, qw))
words2.Add(word);
else if (word.Length == 6 && Check(word, qw))
words3.Add(word);
}
}
Console.WriteLine(string.Join(" ", words1.Count, words2.Count, words3.Count));
HashSet<string> result1 = new HashSet<string>();
HashSet<string> result2 = new HashSet<string>();
HashSet<string> result3 = new HashSet<string>();
using SemaphoreSlim semaphore = new SemaphoreSlim(Environment.ProcessorCount * 2);
List<Task> tasks = new List<Task>();
for (int i = 0; i < words1.Count; i++)
{
await semaphore.WaitAsync();
Console.Write(i + " ");
string w1 = words1[i];
int ii = i;
tasks.Add(Task.Run(() =>
{
for (int j = ii + 1; j < words2.Count; j++)
{
string w2 = words2[j];
for (int k = j + 1; k < words3.Count; k++)
{
string w3 = words3[k];
if (w1[0] == w2[0] && w1[4] == w2[2] && w1[7] == w2[6] && w3[1] == w2[1] && w3[3] == w1[3] && w3[4] == w2[2] && w3[4] == w1[4])
{
lock (result1) result1.Add(w1);
lock (result2) result2.Add(w2);
lock (result3) result3.Add(w3);
}
}
}
semaphore.Release();
}));
}
await Task.WhenAll(tasks);
Console.WriteLine(string.Join(" ", result1.Count, result2.Count, result3.Count));
File.WriteAllLines("words1.txt", result1);
File.WriteAllLines("words2.txt", result2);
File.WriteAllLines("words3.txt", result3);
Console.WriteLine("Done.");
Console.ReadKey();
}
Добавил Console.Write во внешнем цикле, чтобы отслеживать позицию.