Welcome, guest | Sign In | My Account | Store | Cart

icalendar uses its own CaselessDict as the base of many classes. I needed to produce the keys and items in a canonical order, so that certain keys would appear first.

Python, 54 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class CaselessDict(dict):
    """
    A dictionary that isn't case sensitive, and only uses strings as keys.
    Values retain their case.
    """
    # Implementation Omitted

    # A list of keys that must appear first in sorted_keys and sorted_items;
    # must be uppercase.
    canonical_order = None

    def sorted_keys(self):
        """
        Sorts keys according to the canonical_order for the derived class.
        """
        return canonsort_keys(self.keys(), self.canonical_order)

    def sorted_items(self):
        """
        Sorts items according to the canonical_order for the derived class.
        """
        return canonsort_items(self, self.canonical_order)

def canonsort_keys(keys, canonical_order=None):
    """
    Sorts leading keys according to canonical_order.
    Keys not specified in canonical_order will appear alphabetically at the end.

    >>> keys = ['DTEND', 'DTSTAMP', 'DTSTART', 'UID', 'SUMMARY', 'LOCATION']
    >>> canonsort_keys(keys)
    ['DTEND', 'DTSTAMP', 'DTSTART', 'LOCATION', 'SUMMARY', 'UID']
    >>> canonsort_keys(keys, ('SUMMARY', 'DTSTART', 'DTEND', ))
    ['SUMMARY', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'UID']
    >>> canonsort_keys(keys, ('UID', 'DTSTART', 'DTEND', ))
    ['UID', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'SUMMARY']
    >>> canonsort_keys(keys, ('UID', 'DTSTART', 'DTEND', 'RRULE', 'EXDATE'))
    ['UID', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'SUMMARY']
    """
    canonical_map = dict((k, i) for i, k in enumerate(canonical_order or []))
    head = [k for k in keys if k in canonical_map]
    tail = [k for k in keys if k not in canonical_map]
    return sorted(head, key=lambda k: canonical_map[k])  +  sorted(tail)

def canonsort_items(dict1, canonical_order=None):
    """
    Returns a list of items from dict1, sorted by canonical_order.

    >>> d = dict(i=7, c='at', a=3.5, l=(2,3), e=[4,5], n=13, d={'x': 'y'}, r=1.0)
    >>> canonsort_items(d)
    [('a', 3.5), ('c', 'at'), ('d', {'x': 'y'}), ('e', [4, 5]), ('i', 7), ('l', (2, 3)), ('n', 13), ('r', 1.0)]
    >>> canonsort_items(d, ('i', 'c', 'a'))
    [('i', 7), ('c', 'at'), ('a', 3.5), ('d', {'x': 'y'}), ('e', [4, 5]), ('l', (2, 3)), ('n', 13), ('r', 1.0)]
    """
    return [(k, dict1[k]) for k in canonsort_keys(dict1.keys(), canonical_order)]

canonsort_keys computes a dictionary mapping the strings in canonical_order to their index. This canonical_map is used three different ways:

  1. to compute head, an unordered list comprehension of those keys present in canonical_order;
  2. to compute tail, all keys not present in canonical_order;
  3. in the key function used to sort head. (tail is sorted lexicographically.)
Created by George V. Reilly on Fri, 3 Feb 2012 (MIT)
Python recipes (4591)
George V. Reilly's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks