Sprawdź, czy lista zawiera element, który zawiera ciąg i pobierz ten element


146

Szukając odpowiedzi na to pytanie, natknąłem się na podobne z użyciem LINQ, ale nie byłem w stanie ich w pełni zrozumieć (a tym samym zaimplementować), ponieważ nie jestem z nimi zaznajomiony. Zasadniczo chciałbym to zrobić:

  1. Sprawdź, czy którykolwiek element listy zawiera określony ciąg.
  2. Jeśli tak, zdobądź ten element.

Naprawdę nie wiem, jak bym to zrobił. Oto co mogę wymyślić (oczywiście nie działa):

if (myList.Contains(myString))
    string element = myList.ElementAt(myList.IndexOf(myString));

Wiem DLACZEGO to nie działa:

  • myList.Contains()nie zwraca true, ponieważ sprawdza, czy cały element listy pasuje do podanego przeze mnie ciągu.
  • myList.IndexOf() nie znajdzie wystąpienia, ponieważ, jak ma to miejsce ponownie, sprawdzi, czy występuje element pasujący do łańcucha.

Nadal nie mam pojęcia, jak rozwiązać ten problem, ale myślę, że będę musiał użyć LINQ, jak zasugerowano w podobnych pytaniach do mojego. Biorąc to pod uwagę, jeśli tak jest w tym przypadku, chciałbym, aby osoba odpowiadająca wyjaśniła mi użycie LINQ w ich przykładzie (jak powiedziałem, nie przejmowałem się tym w swoim czasie z C #). Z góry dziękuję chłopaki (i dziewczęta?).

EDYCJA: mam rozwiązanie; po prostu przejrzyj listę, sprawdź, czy bieżący element zawiera ciąg, a następnie ustaw ciąg równy bieżącemu elementowi. Zastanawiam się jednak, czy istnieje wydajniejszy sposób niż ten?

string myString = "bla";
string element = "";

for (int i = 0; i < myList.Count; i++)
{
    if (myList[i].Contains(myString))
        element = myList[i];
}

jak wspomniałem w mojej odpowiedzi, staromodne pętle (takie jak Twoje pytanie) są prawie zawsze najszybsze. Ale możesz to przetestować, jeśli tylko ci zależy.
McKay,

Na liście może znajdować się wiele ciągów zawierających Twój ciąg myString, w bieżącej pętli otrzymasz ostatni element. To zależy od Ciebie, czy chcesz znaleźć pierwszą, czy ostatnią, jeśli chcesz znaleźć pierwszą, a następnie przerwij pętlę po znalezieniu przedmiotu.
Habib,

Odpowiedzi:


193

Powinieneś być w stanie użyć Linq tutaj:

var matchingvalues = myList
    .Where(stringToCheck => stringToCheck.Contains(myString));

Jeśli chcesz po prostu zwrócić pierwszy pasujący przedmiot:

var match = myList
    .FirstOrDefault(stringToCheck => stringToCheck.Contains(myString));

if(match != null)
    //Do stuff

+1 - Albo zamienić Whereze FirstOrDefaultw drugim przypadkumyList.FirstOrDefault(stringToCheck => stringToCheck.Contains(myString))
Habib

Świetna odpowiedź. Ale tylko z ciekawości: dlaczego jest to określone przez matchingkompilator ( var)? Skoro wiem, że moja lista jest typu String, czy string matchingw takim przypadku byłoby bezpiecznie jej używać ?
Dimitris Iliadis

1
@JimIliadis „var” i „string” oznaczają dokładnie to samo w tym przypadku - kompilator jest na tyle inteligentny, że wie, że wynikiem może być tylko „ciąg”. var to tak naprawdę kwestia stylu kodowania (jeśli nie używa się typów anonimowych)
Dave Bish

komentując zbyt stary wątek, ale znalazłem jeden wyjątek w tej sprawie. kiedy używasz firstordefault (), upewnij się, że może również zwrócić wartość domyślną. więc załóżmy, że przekazujesz pusty łańcuch, tj. mystring = "" i nie spodziewasz się, że nic się nie pokaże, ale nadal wyświetli pierwszą pozycję z listy, ponieważ wybrałeś pierwszą pozycję domyślną.
Dirty Developer

@DirtyDeveloper Nie jestem pewien, co przez to rozumiesz - Twój przykład zwróciłby „null”, gdyby na liście docelowej nie było pustych ciągów. Masz rację, jeśli próbujesz użyć FirstOrDefault () na typie struktury, np. List <int> - FirstOrDefault () zwróci '0', a nie null - jednak pytanie dotyczyło szczególnie łańcuchów
Dave Bish

29

Podstawowa odpowiedź brzmi: musisz przejść przez pętlę i sprawdzić, czy dowolny element zawiera określony ciąg. Powiedzmy, że kod to:

foreach(string item in myList)
{
    if(item.Contains(myString))
       return item;
}

Równoważny, ale zwięzły kod to:

mylist.Where(x => x.Contains(myString)).FirstOrDefault();

Tutaj x jest parametrem, który działa jak „item” w powyższym kodzie.


12
string result = myList.FirstOrDefault(x => x == myString)
if(result != null)
{
  //found
}

9
for (int i = 0; i < myList.Length; i++)
{
    if (myList[i].Contains(myString)) // (you use the word "contains". either equals or indexof might be appropriate)
    {
        return i;
    }
}

Klasyczne pętle są prawie zawsze najszybsze.


Skoro stawiam na efektywność, czy sugerujesz, że ta metoda jest szybsza (a przez to bardziej wydajna)?
Dimitris Iliadis

2
Nie przetestowałem tego perfekcyjnie, ale myślę, że byłoby to szybsze. Zawsze wymaga tylko jednego przejścia przez listę, dopóki coś nie znajdzie i nie wyjdzie wcześnie (tak jak opcje Linq mogą to zrobić, jeśli są dobrze napisane), nie ma narzutu wywołania metody linq ani narzutu lambda linq. Nie chodzi o to, że są to ogromne powody do niepokoju, ale mogą spowodować spadek wydajności.
McKay

Dlaczego nie używać List.Equals ()?
F8ER

@ V.7 Ponieważ chce tylko wiedzieć, czy jedna pozycja na liście zawiera podciąg. lista.equals nie jest odpowiednim narzędziem do zadania ["abc", "def", "ghi"] zawiera słowo "hi" w sposób, w jaki opisuje to OP. list.equals nawet nie przyjmuje poprawnych typów danych.
McKay

6

Jeśli chcesz listę ciągów zawierających twój ciąg:

var newList = myList.Where(x => x.Contains(myString)).ToList();

Inną opcją jest użycie Linq FirstOrDefault

var element = myList.Where(x => x.Contains(myString)).FirstOrDefault();

Pamiętaj, że Containsmetoda rozróżnia wielkość liter.


1
Dobre przypomnienie o rozróżnianiu wielkości liter, zaimplementuj StringComparison.InvariantCultureIgnoreCase
JoshYates1980

2

Możesz użyć FirstOrDefaultmetody rozszerzenia Linq :

string element = myList.FirstOrDefault(s => s.Contains(myString));

Spowoduje to zwrócenie pierwszego elementu, który zawiera podciąg myString, lub nulljeśli takiego elementu nie zostanie znaleziony.

Jeśli potrzebujesz tylko indeksu, użyj metody List<T>klasy FindIndex:

int index = myList.FindIndex(s => s.Contains(myString));

To zwróci indeks pierwszego elementu, który zawiera podłańcuch myString, lub -1jeśli takiego elementu nie zostanie znaleziony.


2

Wiele dobrych odpowiedzi tutaj, ale używam prostej, używając Exists , jak poniżej:

foreach (var setting in FullList)
{
    if(cleanList.Exists(x => x.ProcedureName == setting.ProcedureName)) 
       setting.IsActive = true; // do you business logic here 
    else
       setting.IsActive = false;
    updateList.Add(setting);
}

2

Powinieneś móc użyć czegoś takiego, dla mnie zadziałało dobrze:

var valuesToMatch = yourList.Where(stringCheck => stringCheck.Contains(myString));

lub coś takiego, jeśli chcesz sprawdzić, gdzie nie pasuje.

 var valuesToMatch = yourList.Where(stringCheck => !stringCheck.Contains(myString));

1

możesz użyć

var match=myList.Where(item=>item.Contains("Required String"));
foreach(var i in match)
{
//do something with the matched items
}

LINQ zapewnia możliwości „odpytywania” dowolnego zbioru danych. Możesz użyć składni takiej jak zapytanie do bazy danych (wybierz, gdzie itp.) W kolekcji (tutaj kolekcja (lista) ciągów).

więc robisz coś w stylu "pobierz mi pozycje z listy, gdzie spełnia dany warunek"

wewnątrz pola Gdzie używasz „wyrażenia lambda”

krótko powiedzieć wyrażenie lambda jest czymś w rodzaju (parametr wejściowy => wartość zwracana)

więc dla parametru „item” zwraca „item.Contains („ wymagany ciąg znaków ”)”. Więc zwraca prawdę, jeśli element zawiera ciąg i tym samym zostanie wybrany z listy, ponieważ spełnił warunek.


1

Aby było to proste, użyj tego;

foreach(string item in myList)//Iterate through each item.
{
 if(item.Contains("Search Term")//True if the item contains search pattern.
 {
   return item;//Return the matched item.
 }
}

Alternatywnie, aby zrobić to z pętlą for, użyj this;

    for (int iterator = 0; iterator < myList.Count; iterator++)
    {
        if (myList[iterator].Contains("String Pattern"))
        {
            return myList[iterator];
        }
    }

Zwracam uwagę, że przegapiłeś nawias końcowy w jednym z wierszy kodu .. if (item.Contains („Search Term”))
Alex

0

Nie widziałem opcji bool w innych odpowiedziach, więc mam nadzieję, że poniższy kod komuś pomoże.

Po prostu użyj Any()

string myString = "test";
bool exists = myList
             .Where(w => w.COLUMN_TO_CHECK.Contains(myString)).Any();

0

Możliwe jest połączenie Any, Where, First i FirstOrDefault; lub po prostu umieść predykat w dowolnej z tych metod, w zależności od tego, co jest potrzebne.

Prawdopodobnie powinieneś unikać używania First, chyba że chcesz, aby wyjątek był zgłaszany, gdy nie zostanie znalezione dopasowanie. FirstOrDefault jest zwykle lepszą opcją, o ile wiesz, że zwróci wartość domyślną typu, jeśli nie zostanie znalezione żadne dopasowanie (wartość domyślna ciągu to null, int to 0, bool to false itd.).

using System.Collections.Generic;
using System.Linq;


bool exists;
string firstMatch;
IEnumerable<string> matchingList;

var myList = new List<string>() { "foo", "bar", "foobar" };

exists = myList.Any(x => x.Contains("o"));
// exists => true

firstMatch = myList.FirstOrDefault(x => x.Contains("o"));
firstMatch = myList.First(x => x.Contains("o"));
// firstMatch => "foo"

firstMatch = myList.First(x => x.Contains("dark side"));
// throws exception because no element contains "dark side"

firstMatch = myList.FirstOrDefault(x => x.Contains("dark side"));
// firstMatch => null

matchingList = myList.Where(x => x.Contains("o")); 
// matchingList => { "foo", "foobar" }

Przetestuj ten kod @ https://rextester.com/TXDL57489

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.