1

Suppose I have a list with the following elements:

times = ['1 day ago', '1 day ago', '1 day ago', '1 day ago', '1 day ago', '7 days ago', '5 months ago', '27 days ago', '7 days ago', '7 days ago', '1 month ago', '1 month ago', '7 days ago', '7 days ago', '7 days ago', '7 days ago', '27 days ago', '1 month ago', '6 hours ago', '22 hours ago', '20 hours ago', '15 hours ago', '1 day ago', '4 days ago', '10 days ago', '8 days ago', '6 days ago', '7 days ago', '8 hours ago', '14 days ago', '14 days ago', '22 days ago', '2 months ago', '2 months ago', '2 months ago']

I am wondering how I can find the entry corresponding to the shortest duration. I have an idea to use a look over days, months, etc. but this feels very inefficient. Does anyone have any ideas? thanks!

khelwood
  • 55,782
  • 14
  • 81
  • 108
user321627
  • 2,350
  • 4
  • 20
  • 43
  • What's the shortest duration in this specific case? What's the expected output? – Riccardo Bucco May 28 '20 at 09:56
  • The shortest duration may be minutes. The expected output would be a single value (accounting for tie-breakers), corresponding to the shortest time – user321627 May 28 '20 at 10:00
  • 1
    @user321627 You need to be more precise what you mean with `1 month ago` (see [this question](https://stackoverflow.com/q/546321/3767239)). Does it mean 30 days ago? – a_guest May 28 '20 at 10:02
  • `shortest = min(norm(elt) for elt in elements)` where `norm()` is a function that turns a string to eg. number of seconds. Looks like splitting the string and multiplying the first element with properly mapped integer value from the second entry would do the trick – EdvardM May 28 '20 at 10:02

4 Answers4

2

You can convert to datetime.timedelta which can be used with min:

from datetime import timedelta

def convert(s):
    n, unit, __ = s.split()
    n = int(n)
    if unit.startswith('month'):  # assuming "1 month" means 30 days
        n *= 30
        unit = 'days'
    if not unit.endswith('s'):
        unit += 's'
    return timedelta(**{unit: n})

Then convert the strings and take the minimum:

deltas = [convert(s) for s in times]
min(deltas)

Or use this method as a key to min:

min(times, key=convert)
a_guest
  • 34,165
  • 12
  • 64
  • 118
1

There you go:

times = ['1 day ago', '1 day ago', '1 day ago', '1 day ago', '1 day ago', '7 days ago', '5 months ago', '27 days ago', '7 days ago', '7 days ago', '1 month ago', '1 month ago', '7 days ago', '7 days ago', '7 days ago', '7 days ago', '27 days ago', '1 month ago', '6 hours ago', '22 hours ago', '20 hours ago', '15 hours ago', '1 day ago', '4 days ago', '10 days ago', '8 days ago', '6 days ago', '7 days ago', '8 hours ago', '14 days ago', '14 days ago', '22 days ago', '2 months ago', '2 months ago', '2 months ago']

def evaluate(time):
    if 'hour' in time:
        return int(time.split(' ')[0])
    if 'day' in time:
        return int(time.split(' ')[0]) * 24
    if 'month' in time:
        return int(time.split(' ')[0]) * 24 * 30

values = [evaluate(time) for time in times]

minValue = min(values)
minIndex = values.index(minValue)

print(minIndex)
print(times[minIndex])
goodvibration
  • 5,980
  • 4
  • 28
  • 61
1

I would redefine the key parameter of the min built-in function:

def value(t):
    x = t.split()
    number = int(x[0])
    number *= (1 if x[1].startswith("hour") else
               24 if x[1].startswith("day") else
               24*30)
    return number

result = min(times, key=value)

Here I suppose that a month lasts 30 days (this is not always the case).

Riccardo Bucco
  • 13,980
  • 4
  • 22
  • 50
1

First you have to parse your input and convert to something readable by your software:

for t in times:
    num, unit, _ = t.split()
    num = int(num)  # here you have an integer

Than you can use a dictionary to convert your values to seconds (in this example) to have the same unit of measure.

units = {"second": 1, "minute": 60, "hour": 3600, ... }

So you can extract your unit:

if unit.endswith("s"):  # remove the plural `s`
    unit = unit[:-1]
converted_unit = units[unit]
seconds_ago = converted_unit * num

And here you have it: you have a single number that you can compare with others, than it is just a matter of finding the minimum. Enjoy!

piertoni
  • 1,933
  • 1
  • 18
  • 30