How to prevent duplicates in random selection?

Question:

I wrote small program to populate my game with NPCs named by random selections from first name and last name lists.
It worked but sometimes there are duplicate names selected. How can I prevent duplicates?

I could use dict but I prefer list. Is this big disadvantage?
The commented block in adding_male_NPC is my attempt to solve this problem.

import random

women_names = ["Jennifer", "Jenna", "Judith", "Becky", "Kelly"]
man_names = ["Adam", "John", "Jack", "Jim", ]
surnames =["Salzinger", "Jefferson", "Blunt", "Jigsaw", "Elem"]
marriage_status = ["Single", "In couple", "Engaged", "Married", "Divorced", "Widow"]
male_NPCs = []
list = []


def clr_list(list):
    del list


def randomizer(list):
    random_choice = random.choice(list)
    clr_list(list)
    return random_choice


def random_male():
    male_surname = randomizer(surnames)
    male_name = randomizer(man_names)
    male_NPC = male_name + " " + male_surname
    return (male_NPC)


def add_one_man():
    male_NPCs.append(random_male())
    return


def addding_male_NPC(count_of_NPC_males):
    while count_of_NPC_males > 1:
        add_one_man()
        # for m in male_NPCs:
        #     unique_count = male_NPCs.count(m)
        #     if unique_count > 1:
        #         male_NPCs.pop(unique)
        #         count_of_NPC_males +=1
        #     else:
        count_of_NPC_males -= 1


count_of_NPC_males = int(input("How many males should create?: "))
addding_male_NPC(count_of_NPC_males)

print(male_NPCs)
print(len(male_NPCs))

So i tried this but its impossible to count strings or somehow don’t use well .count what is most possible.
Get idea to take indexes before creating sum of stings and use it to check double but i feel that i make circles.
I understand that provided list of names and surnames are not guarantee make doubles with high numbers but you got the point of this.

def addding_male_NPC(count_of_NPC_males):
    while count_of_NPC_males > 1:
        add_one_man()
         for m in male_NPCs:
             unique_count = male_NPCs.count(m)
             if unique_count > 1:
                 male_NPCs.pop(unique)
                 count_of_NPC_males +=1
             else:
                  count_of_NPC_males -= 1

Edit
This is so sad 🙁

mylist = ["a", "b", "a", "c", "c"]
mylist = list(dict.fromkeys(mylist))
print(mylist)

But anyway it will cut planned and needed numbers of item. So question is still active. This answer is quite half answer. I wait for better one.

==========================

Yes! I finally found an answer!
Thank to >>Parnav<< ( he is The guy!)
From his suggestion i made code generating from text file more than i can imagine

import random
import itertools

with open('stock_male_names.txt', 'r') as mn, open('stock_female_names.txt', 'r') as wn,  open('stock_surnames.txt', 'r') as sn:
    broken_male_names, broken_female_names, broken_surnames = mn.readlines(), wn.readlines(), sn.readlines()

male_names = [name.strip() for name in broken_male_names]
female_names = [name.strip() for name in broken_female_names]
surnames = [name.strip() for name in broken_surnames]

male_persons = [f"{fname} {lname}" for fname, lname in itertools.product(male_names, surnames)]
female_persons = [f"{fname} {lname}" for fname, lname in itertools.product(female_names, surnames)]

print(male_names)
print(len(male_names)) #1001
print(female_names)
print(len(female_names)) #1000
print(surnames)
print(len(surnames)) #1003
print(male_persons)
print(len(male_persons)) #1004003
print(female_persons)
print(len(female_persons)) #1003000

So from three text files of 1k items i made 1kk unique NPC names in almost no load time with open road to expand.
I am amazingly Happy 🙂
Case closed!

Asked By: beLIEve

||

Answers:

At the end, convert the list of strings into a set and then back to a list to remove any duplicates. Then, use the len() function to determine the length of the list compared to the desired length and call the function again this time adding to the list.

Answered By: Prime Price

First, we want all possible combinations of the first and last names. We can get this using itertools.product:

import itertools
import random

male_names = [f"{fname} {lname}" for fname, lname in itertools.product(man_names, surname)]

print(male_names)
# ['Adam Salzinger', 'Adam Jefferson', ..., 'John Salzinger', 'John Jefferson', ..., 'Jim Jigsaw', 'Jim Elem']

Since you want to randomly get names from this list, shuffle it.

random.shuffle(male_names)
print(male_names)
# ['Jim Jefferson', 'Jack Jigsaw', 'Adam Jefferson', ..., 'Adam Blunt', 'John Blunt']

Every time you want to add a NPC, pop the last element from this list. Since you shuffled the list earlier, you’re guaranteed a random element even if you always pop the last element. Popping the element removes it from the list, so you don’t have to worry about duplicates. Take care not to pop more than the list contains (you have indicated elsewhere that this isn’t a problem). I prefer to pop from the end of the list because that is an O(1) operation. Popping from a different location would be more expensive.

def add_male_npcs(count=1):
    for _ in range(count):
        male_NPCs.append(male_names.pop())
Answered By: Pranav Hosangadi
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.