2
\$\begingroup\$

I have a Django site where users upload and have the option to delete files. There will be a lot of files that will have the same logical name so I append numbers to them:

foo/bar/file.mp3 foo/bar/file2.mp3...

I needed a method that would return the lowest file number not currently being used. I have a working example here, but it feels wrong and ugly and I don't know if there is an easier way of doing it. If I ran the method on the examples above, it would return "1" so I know I can make the next file foo/bar/file1.mp3.

def get_num(self, files):
        """function to extract the lowest file number to save as, returns string. Takes a list as input"""
        nums = []
        for f in files:
            # Call the unicode function which should return the string path
            match = re.findall("([\d]+)\.[\w]+", f.__unicode__())
            if match:
                nums.append(int(match[0]))
            else:
                nums.append(0)

        for i in range(len(nums) + 1):
            if i not in nums:
                if i == 0:
                    return ""
                else:
                    return str(i)
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Could you use a random string generator using 15 digits or something like that? This would give you a unique key with less computational overhead and provided that you give enough digits the odds of duplication are extremely slim. You have a set string size so that's easy enough to parse off on document display and download. (I'm assuming you're already parsing off the number identifier) \$\endgroup\$
    – Trojan404
    Aug 23, 2016 at 3:36

2 Answers 2

5
\$\begingroup\$

When you don't need indexes or ordering, it is generally a good idea to use a set over a list because its membership testing is more efficient. That means, use nums = set() instead of nums = [], and use nums.add(...) instead of nums.append(...). That's all you need to change.


I'm not sure why you are accessing the special method directly. Why not use unicode(f) instead of f.__unicode__()?


Python has its own version of something that is often called a ternary operator. Its format is truth_value if some_condition else false_value. Therefore, you can replace your first if and else blocks with this:

nums.append(int(match[0]) if match else 0)

(It would be nums.add(...) if you decide to use a set.)

and your second if and else blocks with this:

return "" if i == 0 else str(i)
\$\endgroup\$
2
0
\$\begingroup\$

Test file existence, testing numbers with ever larger steps.

In pseudo-code, for powers of two (but three would be nice too, or fibonacci numbers):

find free file number (name)
     find file with number (name, 1, 10_000)

""" start: assumed existing, end: assumed not existing """
find file with number (name, start, end)
    if start + 1 >= end then end
    for i in 1, 2, 4, 8, 16, 32, ... where start + i < end
        find where file with number=(start + i) exists
               and file with number=(start + next i) >= end or does not exist
            then find file with numer (name, start + i, start + next i)

Worst case: \$(^2\log n)^2/2\$ hence \$O((\log n)^2)\$

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.