Home > Back-end >  Check if string exists in list made with data from a csv file
Check if string exists in list made with data from a csv file

Time:05-14

I import data from a csv file and then store it in a list (List<Customer> customers). The data is added to the list by creating and adding objects. The objects are created using constructors which take the contents of the file as parameters, everything is string. The list is of type Customer from what I understand. I want to make an if statement to check whether user input (string) exists in the list.

I tried this code:

ImportData("CustomerData.csv");

Console.WriteLine("Please provide customer name:");
string customer_Name = Console.ReadLine();

//if (customers.Contains(customer_Name))
bool b = customers.Contains(customer_Name);
//if (customers.Any(customers.Contains(customer_Name)))
if (b)

Nothing works, I get the error CS1503 Argument 1: cannot convert from 'string' to 'MyNameSpace.Customer'. I don't really understand what it means. I think maybe it is because of the list being of <Customer> type? I also have a class called Customer. I tried to change the list type to <string>, it didn't help.

CodePudding user response:

customers is a List<T> where T is Customer, your error is telling you that it can't convert a string to the class type Customer, what I think you want is to use Linq to look for any Customer objects who's property of xxx (Customer Name) equals the user input, you could do this, where you'd replace somestringproperty with your classes property name:

bool b = customers.Any(a => a.somestringproperty == customer_Name);

CodePudding user response:

List.Contains requires you to supply a customer object. As you've likely NOT overridden Equals and GetHashcode to allow two different instances of customer to be considered equal, it's probably pointless to use, as it would need you to find an instance of a customer you wanted in the list, then ask the list if it Contains the instance you found (which you already know)

IEnumerable.Any requires you to supply a method or small function (often called a lambda) that operates on a customer object and returns true or false. This is more useful to your situation

The short form looks like:

if (customers.Any(c => c.CustomerName == customer_Name))

The c => c.CustomerName == customer_Name is a mini method (lambda):

  • c the argument to the method; implicitly a Customer (hence why I named it c) for reasons I'll go into in a moment
  • => the separator between arguments and body; C# uses it to understand that youre making a lambda
  • c.CustomerName == customer_Name a statement that resolves to a boolean

In long form it might look like:

bool SomeMethod(Customer c){
  return c.CustomerName == customer_Name;
}

We can shorten this long form to the short form because the compiler can figure out a lot of the stuff:

  • It's a rule that Any needs something that returns a boolean, so we don't need the bool.
  • It's implied that the input argument c is a Customer because the List is full of Customers.
  • We can ditch the { return ... } because C# has expression bodies; "arguments on the left, then a =>, then a single statement that resolves to the return value type we first mentioned" means we're allowed to skip using curly brackets and the return keyword
  • When writing a lamdba we don't need to give it a name, because we never refer to it by name anywhere; it is purely embodied by its definition and only used by the one thing we give it to, so it can remain nameless

So our long form gradually boils down:

//long form: a class level method
bool SomeMethod(Customer c){
  return c.CustomerName == customer_Name;
}

//shorter: expression body; this is still valid C# to put in a class and you could write all your "one line return" methods like this
bool SomeMethod(Customer c) => c.CustomerName == customer_Name;

//shortest: a lambda, this has to be stored in a variable or used in a method call that accepts a lambda - you cannot write this in a class level statemetn because the compiler needs some surrounding context to figure bits of it out
c => c.CustomerName == customer_Name

If you take a look at the docs for Any it tells you waht you have to supply:

enter image description here

Here's how to decipher this:

Any is a method that can be called on an IEnumerable. List is IEnumerable, so Any can be called on a List. A List is full of objects that have a particular type, TSource. TSource would be replaced by whatever is in the list, in this case a Customer for you. The word this means it's an extension method; a trick that means it looks like you can call it directly on a List of customers:

//you could do this; call Any and pass customers in
Enumerable.Any(customers, ...)

//but its more normal, readable etc to do this:
customers.Any(...)

The use of this on the first argument allows you to write like the latter, and the compiler will understand it to be the former. It lets you "make it look like you added a method to the thing youre calling it on, even though you didn't"

The more interesting part is the Func<TSource,bool> argument. This means "A function that takes a TSource and returns a bool". Remember, TSource for your List<Customer> means "a Customer".

The last thing mentioned is the return type, everything else is an argument to a method. If you saw Funct<string, int, TSource, DateTime> it would mean "A function or method that has 3 input arguments; a string, int and TSource (Customer) in taht order, and must return a DateTime". Any method that matches this signatore is allowed to be supplied.

Circling back to Funct<TSource, bool> for Any; you have to supply a method that takes a TSource (Customer, in your case) and returns a bool.

This matches that:

private string customer_Name = "john" //class level variable

//method that matches the rule "takes a Customer, returns a bool"
bool SomeMethod(Customer c){
  return c.CustomerName == customer_Name;
}

//can be passed to list's Any
customers.Any(SomeMethod)

This shorter form also matches that:

customers.Any(c => c.CustomerName == customer_Name)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        also a method that "takes a customer, returns a bool"

Any will take the method you give it and start looping the list, calling the method and passing in the current customer it is looping. As soon as it gets a true it stops and returns true, because it has discovered that "Any customer matches ..." is true


I get the error CS1503 Argument 1: cannot convert from 'string' to 'MyNameSpace.Customer'. I don't really understand what it means

Hopefully now you can see, if you do:

customers.Any("John")

customers.Any needs you to provide "a method that takes a Customer and returns a bool" a.k.a Func<Customer,bool> - you cannot provide a string like "John" because a string is not a Func<Customer,bool>..

..and think about it. If you did provide a string like "John" - how does C# know that's the name you want to look up, and not the address?

Providing a method as an argument rather than providing data as an argument is perhaps the biggest thing to get your head round here; why do Microsoft write code that accepts methods instead of data? Because simply put, they cannot possibly code for all the variations of how people will use List, so instead they just say "We'll loop the list, calling some bit of code we cannot imagine - the bit that you supply - and if your code returns true, we'll return true"

It's natural that Microsoft write C# but there are bits missing that only you can know - hence why "passing methods around like data" is vital

  • Related