6

I am looking to select one item from a set. It does not matter which item it is, but I want it to be the same item every time the function is called.

For example, if I had the set:

my_set = set(["ABC","ABC inc", "ABC ltd"])

Then I want to return any one of those, but the same one whenever the function is run. [i.e. so I would always get "ABC", or always get "ABC inc" whenever I ran with the above set.] If I was using lists, I would just take the first item, but sets don't have a first per se.

This is in contrast to selecting randomly [e.g. How do I pick 2 random items from a Python set? ] where it will change every time it is run.

Brad Koch
  • 19,267
  • 19
  • 110
  • 137
kyrenia
  • 5,431
  • 9
  • 63
  • 93
  • 1
    your call to `set` gives an error since it expects an iterable, not three of them – Pynchia Sep 10 '15 at 21:53
  • You can't, since a set is unordered. The only way I can think of is to compare the hash of each element until you find the element with the same hash as you had before. –  Sep 10 '15 at 21:54
  • I think [this answer][1] can help you. [1]: http://stackoverflow.com/questions/59825/how-to-retrieve-an-element-from-a-set-without-removing-it – jlnabais Sep 10 '15 at 21:54
  • @Pynchia - sorry about that - corrected – kyrenia Sep 10 '15 at 21:55
  • 3
    Why are you using a set for this? If there's a good reason, use a set backed by a tree or heap. – Marcin Sep 10 '15 at 21:58
  • this doesn't sound like the right way to approach whatever problem you're trying to solve... – acushner Sep 10 '15 at 22:07
  • @acushner - working on old code - I already have the data I want to extract from in a set format - they are all alternative ways of writing the same company name - currently just need to consistently select the same value every time i run. – kyrenia Sep 10 '15 at 22:14
  • The *right* way to do it is to define an an ordering scheme and then use a form of ordered set as in [this recipe](http://code.activestate.com/recipes/576694/) – dawg Sep 10 '15 at 22:18

7 Answers7

7

What about converting to list and sorting?

my_list = list(my_set)
my_list.sort()
chosen_element = my_list[0]
dawg
  • 98,345
  • 23
  • 131
  • 206
dorverbin
  • 467
  • 1
  • 4
  • 17
3

you could use a function with memoization

def get_random(my_set,memo={}):
    if id(my_set) not in memo:
       memo[id(my_set)] = random.choice(list(my_set))
    return memo[id(my_set)]

a_set = set([1,2,3,4,5])
print get_random(a_set)
print get_random(a_set)

this would always give you the same value as long as you passed in a_set ... (a different set would give a different answer)

if you wanted to make sure the item was still in the set you could change the memo if check

def get_random(my_set,memo={}):
    if id(my_set) not in memo or memo[id(my_set)] not in my_set:
       memo[id(my_set)] = random.choice(list(my_set))
    return memo[id(my_set)]
Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
  • 3
    Dangerous in so many ways. E.g., remove the memoized item from the set, and it will still show up as the answer. (You'll also need to make a list of `my_set` before applying `random.choice`.) – Alan Sep 10 '15 at 22:06
  • 1
    meh its what OP asked for ... you could always do something like `or memo[id(my_set)] not in my_set` – Joran Beasley Sep 10 '15 at 22:09
  • ... I very quickly ran into issues using this [e.g. when re-initialized the set with new value, the prior result was returned] – kyrenia Sep 10 '15 at 22:42
  • 1
    if the memory location is the same then its the same set and it returns to you the first value it returned ... ... which is what was requested ... anyway I altered it to check for the items existance still in the set ... – Joran Beasley Sep 10 '15 at 22:45
  • If memory is also an issue and you don't want to duplicate the set in memory, you could grab the first value from an iterator rather than random.choice(list(my_set)), which is an advantage of this approach rather than generating a list. – Alex Huszagh Sep 10 '15 at 23:08
1

I may have misunderstood your question, but a check for membership in the set should work, if you are looking for anything specific.

letter_set = set(['abc','ABC','xyz','XYZ'])
check_string = 'ABC'
if check_string in letter_set:
    output = check_string

This should only give an output value if the desired string is in the set.

Alea Kootz
  • 913
  • 4
  • 11
0

Use min().

>>> my_set = set(["ABC","ABC inc", "ABC ltd"])
>>> min(my_set)
'ABC'

Raises ValueError if empty. From Python 3.4 a default value can be returned instead.

Mattias Wallin
  • 1,418
  • 1
  • 10
  • 8
0

Another answer I found for this is to use min:

elem = min(mySet)
cumin
  • 471
  • 5
  • 17
0

Maybe this can help you:

from iteration_utilities import first
from simple_benchmark import benchmark

def forLoop(s):
    value = len(s)/2
    for elem in s:
        if elem == value:
            return elem


def RemoveAndSet(s: set):
    value = len(s)/2
    s.remove(value)
    s.add(value)


argumente = {2 ** i: set(range(2 ** i)) for i in range(10, 13)}
a = benchmark([forLoop, RemoveAndSet],
              argumente,
              function_aliases={first: 'First'})

print(a)

Output:

         forLoop       RemoveAndSet
   32    8.139000e-07  4.400154e-07
   64    1.403343e-06  5.584737e-07
   128   2.584880e-06  5.288382e-07
   256   4.903100e-06  4.909571e-07
   512   9.628500e-06  5.034354e-07
   1024  1.899860e-05  4.541565e-07
   2048  3.760350e-05  5.803184e-07

Or that: https://pypi.org/project/ordered-set/

Practical
  • 11
  • 3
-1

Sets are implemented as hash tables. So the order is deterministic unless you insert/remove an element.

say x = set (['1','123','2','10'])

this set will always have an order '1', '2', '123', '10' (lexicographic I guess)

but if you are inserting or removing an element then you will have to use some sort of sorting function to make the order deterministic

hackbot89
  • 41
  • 1
  • 5
  • 3
    *Never* count on a set iterating through its elements in a certain order. – Alan Sep 10 '15 at 22:22
  • I think you should be good as long as you are using pop to pull the elements out, because after a pop the order still remains the same. Do you have an example that explains why I should "Never count ... " ? : ) – hackbot89 Sep 10 '15 at 22:27
  • Sort of ... http://stackoverflow.com/questions/3848091/python-set-iteration-order-varies-from-run-to-run – Alan Sep 11 '15 at 02:12