How to sort JSON file by value Python

Question:

I have a big JSON file that contains timezones like the following:

{
   "timezones":[
      {
         "id":1,
         "name":"(UTC) Coordinated Universal Time",
         "utc_offset":"+0"
      },
      {
         "id":2,
         "name":"(UTC+8:45) Australian Central Standard Time",
         "utc_offset":"+8:45"
      },
      {
         "id":3,
         "name":"(UTC-5) Acre Time",
         "utc_offset":"-5"
      },
      {
         "id":4,
         "name":"(UTC+8:45) Australian Central Western Standard Time",
         "utc_offset":"+8:45"
      }
   ]
}

I’m trying to sort it according to the utc_offset values from - to +, if the utc_offset are identical, then by name alphabetically. So The wanted result should be like this:

{
   "timezones":[
      {
         "id":3,
         "name":"(UTC-5) Acre Time",
         "utc_offset":"-5"
      },
      {
         "id":1,
         "name":"(UTC) Coordinated Universal Time",
         "utc_offset":"+0"
      },
      {
         "id":2,
         "name":"(UTC+8:45) Australian Central Standard Time",
         "utc_offset":"+8:45"
      },
      {
         "id":4,
         "name":"(UTC+8:45) Australian Central Western Standard Time",
         "utc_offset":"+8:45"
      }
   ]
}

Using the following code I tried:

lines = lines.sort(int(json['timezones']['utc_offset']), reverse=True)

print(lines)

But it’s showing an error AttributeError: 'dict' object has no attribute 'sort'

How I may do this?

Asked By: Anastasia Quart

||

Answers:

json = {
   "timezones":[
      {
         "id":1,
         "name":"(UTC) Coordinated Universal Time",
         "utc_offset":"+0"
      },
      {
         "id":2,
         "name":"(UTC+8:45) Australian Central Standard Time",
         "utc_offset":"+8:45"
      },
      {
         "id":3,
         "name":"(UTC-5) Acre Time",
         "utc_offset":"-5"
      },
      {
         "id":4,
         "name":"(UTC+8:45) Australian Central Western Standard Time",
         "utc_offset":"+8:45"
      }
   ]
}

def to_mins(e):
    offset = e['utc_offset']
    sign = 1 if offset[0] == '+' else -1
    hm = offset[1:].split(':')
    h, m = (hm[0], 0) if len(hm) == 1 else hm
    return sign * (60 * int(h) + int(m))



print(sorted(json['timezones'], key=to_mins))

prints

[{'id': 3, 'name': '(UTC-5) Acre Time', 'utc_offset': '-5'},
 {'id': 1, 'name': '(UTC) Coordinated Universal Time', 'utc_offset': '+0'},
 {'id': 2,
  'name': '(UTC+8:45) Australian Central Standard Time',
  'utc_offset': '+8:45'},
 {'id': 4,
  'name': '(UTC+8:45) Australian Central Western Standard Time',
  'utc_offset': '+8:45'}]
Answered By: Michael Hodel

Maybe something like this using regexps for parsing utc_offset:

adict = {
    "timezones": [
        {
            "id": 1,
            "name": "(UTC) Coordinated Universal Time",
            "utc_offset": "+0"
        },
        {
            "id": 2,
            "name": "(UTC+8:45) Australian Central Standard Time",
            "utc_offset": "+8:45"
        },
        {
            "id": 3,
            "name": "(UTC-5) Acre Time",
            "utc_offset": "-5"
        },
        {
            "id": 4,
            "name": "(UTC+8:45) Australian Central Western Standard Time",
            "utc_offset": "+8:45"
        }
    ]
}

timezones = adict['timezones']

pattern = re.compile(r'^(?P<sign>[+-])(?P<hour>d+):?(?P<minutes>d+)?')


def get_time(string):
    m = pattern.match(string)
    if not m:
        return False
    sign, hour, minutes = m.groupdict().values()
    result = int(hour) * 60 + (int(minutes) if minutes else 0)
    result = -result if sign == '-' else result
    return result


print(sorted(timezones, key=lambda x: (get_time(x['utc_offset']), x['name'])))

Output:

[
    {'id': 3, 'name': '(UTC-5) Acre Time', 'utc_offset': '-5'},
    {'id': 1, 'name': '(UTC) Coordinated Universal Time', 'utc_offset': '+0'},
    {'id': 2, 'name': '(UTC+8:45) Australian Central Standard Time', 'utc_offset': '+8:45'},
    {'id': 4, 'name': '(UTC+8:45) Australian Central Western Standard Time', 'utc_offset': '+8:45'}
]
Answered By: funnydman

So first we transform the timezone strings into numeric values, then sort using the sorted function.
If you want to get the results in descending order, you can change the value of the boolean parameter reverse.

def get_minutes_tz(str_tz):
    min_tz = None
    # add more checks on the format of the timezone string (you can also use regex)
    if len(str_tz)>0 and isinstance(str_tz, str) and (str_tz.startswith('+')  or str_tz.startswith('-')):
        splits = str_tz[1:].split(':')
        min_tz = int(str_tz[0] + str(int(splits[0])*60 if len(splits)>0 else 0 + int(splits[1] if len(splits)>1 else 0)))
    return min_tz

sorted(d['timezones'], key=lambda k: get_minutes_tz(k['utc_offset']), 
       reverse=False)

result:

[{'id': 3, 'name': '(UTC-5) Acre Time', 'utc_offset': '-5'},
 {'id': 1, 'name': '(UTC) Coordinated Universal Time', 'utc_offset': '+0'},
 {'id': 2,
  'name': '(UTC+8:45) Australian Central Standard Time',
  'utc_offset': '+8:45'},
 {'id': 4,
  'name': '(UTC+8:45) Australian Central Western Standard Time',
  'utc_offset': '+8:45'}]
Answered By: Massifox
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.