0

I have a list of multiple places to stay. What I am struggling to do however is have a user input a string and then searches the list and prints it (i.e. typing Bec will show a result for Becketts)

This is the list:

('Becketts', 'Bed and Breakfast', '11 Bellevue Terrace Southsea Portsmouth PO5 3AT')
('Charles Hope Apartments', 'Apartment', 'Exchange Court Southampton SO14 3SB')
('Claremont Guest House', 'Bed and Breakfast', '33-35 The Polygon Southampton SO15 2BP', 8)
('Farmhouse Hotel', 'Hotel', 'Burrfields Rd Portsmouth PO3 5HH')
('Ferry House Lodge', 'Bed and Breakfast', '472 Mile End Rd Portsmouth PO2 7BX')
('Freemantle Solent Lodge', 'Bed and Breakfast', 'Park Rd Freemantle Southampton SO15 3BB')
('Hammersmith Rooms', 'Hostel', '28-30 Bute Gardens London, W6 7DS')

They code for the search function I am trying is this:

And my result is always an empty list

def search_name():
    response = input()
    responses = [match for match in places if response in match]
    print(responses)
2
  • Your function doesn't return anything, when you say "result", do you mean it prints an empty list? Assuming that places is a list of tuples (it's not clear from the bits of code you shared), that makes sense - the input will be a string and there's no literal 'Bec' in any of the tuples, there is however the substring 'Bec' in some of the elements of the tuples. Commented Dec 12, 2023 at 21:23
  • The question was closed, but the linked question is not in fact addressing the problem OP has. The linked question is about finding a substring in a list, however OP is making the mistake of missing the fact that they're performing in on a tuple, when they really want to perform that operation on all the members of the tuple. Commented Dec 12, 2023 at 21:27

1 Answer 1

2

It's always a good idea to share an example people can run, if possible, like this:

places = [
    ('Becketts', 'Bed and Breakfast', '11 Bellevue Terrace Southsea Portsmouth PO5 3AT'),
    ('Charles Hope Apartments', 'Apartment', 'Exchange Court Southampton SO14 3SB'),
    ('Claremont Guest House', 'Bed and Breakfast', '33-35 The Polygon Southampton SO15 2BP', '8'),
    ('Farmhouse Hotel', 'Hotel', 'Burrfields Rd Portsmouth PO3 5HH'),
    ('Ferry House Lodge', 'Bed and Breakfast', '472 Mile End Rd Portsmouth PO2 7BX'),
    ('Freemantle Solent Lodge', 'Bed and Breakfast', 'Park Rd Freemantle Southampton SO15 3BB'),
    ('Hammersmith Rooms', 'Hostel', '28-30 Bute Gardens London, W6 7DS'),
]


def search_name():
    response = input()
    responses = [match for match in places if response in match]
    print(responses)


search_name()

Note that there are many questions explaining how to find a substring in a list (or tuple for that matter) of strings, like How to check if a string is a substring of items in a list of strings

But you'll find that your function prints nothing when you enter 'Bec', but if you try entering 'Becketts', it does in fact find a result. This is an important clue to what the problem is.

The expression 'Bec' in ('Becketts', 'Bed and Breakfast') is False, since 'Bec' is not in the tuple. But you wanted to check if 'Bec' was part of any string in the tuple. So:

    responses = [match for match in places for item in match if response in item]

With this line, the code works better.

But note that the solution doesn't quite work as expected yet: try entering 'mantle'. You'll get the Freemantle data twice. For a more precise solution:

responses = [match for match in places if any(response in item for item in match)]

This works differently, because it only loops len(places) iterations, and for each it checks if the response is in any of the items in each tuple - and then the tuple is added.

The previous response looks a bit simpler, but that loops over places and then each item in each of those places, so it loops for the sum of all tuple lengths iterations - and it can add each tuple as many times as it has elements. Probably not what you want.

By the way, your data has a problematic 8 as one of the tuple values. I've changed that into a string '8' but if you need to deal with the integer there, you'll need to check for it in the function. Otherwise, performing in on it will fail.

A quick fix for the problem with the 8 would be this (but this only works if all of the values can be converted to a str):

responses = [match for match in places if any(response in str(item) for item in match)]

Similar to what @shadowranger indicated in the comments, if you're only interested in matching part of the name (or whatever you consider the first field in the tuple to be), this would be a good solution:

responses = [(name, *_) for (name, *_) in places if response in name]

Here, the second (name, *_) unpacks the tuple into a name and 'the rest' in an unnamed variable, and after checking if response is in name, the first (name, *_) ensures the entire tuple is included in the result.

Sign up to request clarification or add additional context in comments.

3 Comments

Hey! Sorry for my bad formatting, I am not used to this website yet. I appreciate your quick and detailed answer! It was very helpful!
Note: If the intent was only to search the first field, not all the fields, you'd want something like responses = [(name, cat, addr) for name, cat, addr in places if response in name]. Unpacking isn't strictly necessary (responses = [place for place in places if response in places[0]] works too), but it means you get self-documenting names, rather than indexing to a magic number, and it verifies that each element has exactly three fields, no more, no less, so that assumption being violated fails loudly, rather than being ignored.
You're welcome @charizard75 - if you feel the question has been answered, consider clicking the checkmark next to the answer, so your question no longer appears as unanswered.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.