<?xml version="1.0" encoding="utf-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>
<channel>
	<title>Comments on: A sorting problem</title>
	<atom:link href="http://blog.dowski.com/2007/04/12/a-sorting-problem/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/</link>
	<description>Unfortunately, Christian had a Thwart, and the Magpie stayed in play.</description>
	<pubDate>Sun, 20 Jul 2008 18:36:00 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
		<item>
		<title>By: Thomas</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109308</link>
		<dc:creator>Thomas</dc:creator>
		<pubDate>Sat, 14 Apr 2007 07:14:51 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109308</guid>
		<description>Well, I've noticed this change in semantics, but that's how I apparently wrongly interpreted "ordered first by the known field names, and then by any derived field names". I had understood that the suffixes should be used as the secondary sort criteria. Of course I should have mentioned that.

But, anyway, an awesome_sort3 with reintroduced restore_natural_order, but with the faster field_mapping2 and the sum idiom replaced by a faster alternative too, ie

def flatten(iteratable_of_lists):
..result = []
..for lst in iteratable_of_lists:
....result.extend(lst)
..return result

def awesome_sort3(keys, specific_order):
..mapping = field_mapping2(keys, specific_order)
..mapping = restore_natural_order(mapping, keys, specific_order)
..# Join the child fields, ordered by their specific order
..return flatten(mapping[k] for k in specific_order)

has the correct semantics and is still much faster than the original solution:

original:
awesome_sort 7500 keys =&#62; 0.341000080109 sec
awesome_sort 15000 keys =&#62; 1.20199990273 sec
awesome_sort 22500 keys =&#62; 3.30400013924 sec
awesome_sort 30000 keys =&#62; 8.97299981117 sec

1st improved solution, wrong semantics:
awesome_sort2 7500 keys =&#62; 0.0400002002716 sec
awesome_sort2 15000 keys =&#62; 0.0899999141693 sec
awesome_sort2 22500 keys =&#62; 0.121000051498 sec
awesome_sort2 30000 keys =&#62; 0.169999837875 sec

solution from above, correct semantics;
awesome_sort3 7500 keys =&#62; 0.0500001907349 sec
awesome_sort3 15000 keys =&#62; 0.119999885559 sec
awesome_sort3 22500 keys =&#62; 0.22000002861 sec
awesome_sort3 30000 keys =&#62; 0.300999879837 sec</description>
		<content:encoded><![CDATA[<p>Well, I&#8217;ve noticed this change in semantics, but that&#8217;s how I apparently wrongly interpreted &#8220;ordered first by the known field names, and then by any derived field names&#8221;. I had understood that the suffixes should be used as the secondary sort criteria. Of course I should have mentioned that.</p>
<p>But, anyway, an awesome_sort3 with reintroduced restore_natural_order, but with the faster field_mapping2 and the sum idiom replaced by a faster alternative too, ie</p>
<p>def flatten(iteratable_of_lists):<br />
..result = []<br />
..for lst in iteratable_of_lists:<br />
&#8230;.result.extend(lst)<br />
..return result</p>
<p>def awesome_sort3(keys, specific_order):<br />
..mapping = field_mapping2(keys, specific_order)<br />
..mapping = restore_natural_order(mapping, keys, specific_order)<br />
..# Join the child fields, ordered by their specific order<br />
..return flatten(mapping[k] for k in specific_order)</p>
<p>has the correct semantics and is still much faster than the original solution:</p>
<p>original:<br />
awesome_sort 7500 keys =&gt; 0.341000080109 sec<br />
awesome_sort 15000 keys =&gt; 1.20199990273 sec<br />
awesome_sort 22500 keys =&gt; 3.30400013924 sec<br />
awesome_sort 30000 keys =&gt; 8.97299981117 sec</p>
<p>1st improved solution, wrong semantics:<br />
awesome_sort2 7500 keys =&gt; 0.0400002002716 sec<br />
awesome_sort2 15000 keys =&gt; 0.0899999141693 sec<br />
awesome_sort2 22500 keys =&gt; 0.121000051498 sec<br />
awesome_sort2 30000 keys =&gt; 0.169999837875 sec</p>
<p>solution from above, correct semantics;<br />
awesome_sort3 7500 keys =&gt; 0.0500001907349 sec<br />
awesome_sort3 15000 keys =&gt; 0.119999885559 sec<br />
awesome_sort3 22500 keys =&gt; 0.22000002861 sec<br />
awesome_sort3 30000 keys =&gt; 0.300999879837 sec</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Gary Bernhardt</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109301</link>
		<dc:creator>Gary Bernhardt</dc:creator>
		<pubDate>Sat, 14 Apr 2007 04:30:28 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109301</guid>
		<description>Well done, Thomas.  I wasn't thinking a whole lot about implementation - academic as this exercise is, I was thinking "given an ideal list implementation...", etc.  The sum and pop optimizations were good finds.

However, I think you need to keep restore_natural_order.  Your results don't match the originals:

&#62;&#62;&#62; sorted(['id', 'id_old', 'id_new'], cmp_headers) # Christian's original method
['id', 'id_old', 'id_new']
&#62;&#62;&#62; awesome_sort(['id', 'id_old', 'id_new']) # matches original
['id', 'id_old', 'id_new']
&#62;&#62;&#62; awesome_sort2(['id', 'id_old', 'id_new'], specific_order) # doesn't match - new and old are switched
['id', 'id_new', 'id_old']

That happens because the lists that come out of the mapping function will be sorted alphanumerically.  Christian's original function sorted the keys by the specific order first, and by the original order of the child keys second.  That's what restore_natural_order fixes.</description>
		<content:encoded><![CDATA[<p>Well done, Thomas.  I wasn&#8217;t thinking a whole lot about implementation - academic as this exercise is, I was thinking &#8220;given an ideal list implementation&#8230;&#8221;, etc.  The sum and pop optimizations were good finds.</p>
<p>However, I think you need to keep restore_natural_order.  Your results don&#8217;t match the originals:</p>
<p>&gt;&gt;&gt; sorted(['id', 'id_old', 'id_new'], cmp_headers) # Christian&#8217;s original method<br />
['id', 'id_old', 'id_new']<br />
&gt;&gt;&gt; awesome_sort(['id', 'id_old', 'id_new']) # matches original<br />
['id', 'id_old', 'id_new']<br />
&gt;&gt;&gt; awesome_sort2(['id', 'id_old', 'id_new'], specific_order) # doesn&#8217;t match - new and old are switched<br />
['id', 'id_new', 'id_old']</p>
<p>That happens because the lists that come out of the mapping function will be sorted alphanumerically.  Christian&#8217;s original function sorted the keys by the specific order first, and by the original order of the child keys second.  That&#8217;s what restore_natural_order fixes.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Thomas</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109263</link>
		<dc:creator>Thomas</dc:creator>
		<pubDate>Fri, 13 Apr 2007 19:04:42 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109263</guid>
		<description>For the record, here is how I create the test data:


def random_permutation(lst):
    """Permutes lst by swapping random entries."""
    def swap(lst, i, j):
        tmp = lst[j]
        lst[j] = lst[i]
        lst[i] = tmp
    import random
    random.seed(0)
    result = list(lst)
    n = len(result)
    for i in range(n):
        j = random.randint(0, n-1)
        swap(result, i, j)
    return result

def generate_dataset(n, suffixes):
    """Generates dataset as a pair (keys, specific_order).

    &#62;&#62;&#62; generate_dataset(2, ["a", "b"])
    (['1/', '1/b', '0/a', '0/b', '0/', '1/a'], ['1/', '0/'])
    """
    specific_order = []
    keys = []
    for i in range(n):
        prefix = str(i) + "/" # to assure that no prefix is a prefix of another prefix
        specific_order.append(prefix)
        for suffix in suffixes:
            keys.append(prefix + suffix)
    keys.extend(specific_order)
    return random_permutation(keys), random_permutation(specific_order)
</description>
		<content:encoded><![CDATA[<p>For the record, here is how I create the test data:</p>
<p>def random_permutation(lst):<br />
    &#8220;&#8221;"Permutes lst by swapping random entries.&#8221;"&#8221;<br />
    def swap(lst, i, j):<br />
        tmp = lst[j]<br />
        lst[j] = lst[i]<br />
        lst[i] = tmp<br />
    import random<br />
    random.seed(0)<br />
    result = list(lst)<br />
    n = len(result)<br />
    for i in range(n):<br />
        j = random.randint(0, n-1)<br />
        swap(result, i, j)<br />
    return result</p>
<p>def generate_dataset(n, suffixes):<br />
    &#8220;&#8221;"Generates dataset as a pair (keys, specific_order).</p>
<p>    &gt;&gt;&gt; generate_dataset(2, ["a", "b"])<br />
    (['1/', '1/b', '0/a', '0/b', '0/', '1/a'], ['1/', '0/'])<br />
    &#8220;&#8221;"<br />
    specific_order = []<br />
    keys = []<br />
    for i in range(n):<br />
        prefix = str(i) + &#8220;/&#8221; # to assure that no prefix is a prefix of another prefix<br />
        specific_order.append(prefix)<br />
        for suffix in suffixes:<br />
            keys.append(prefix + suffix)<br />
    keys.extend(specific_order)<br />
    return random_permutation(keys), random_permutation(specific_order)</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Thomas</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109262</link>
		<dc:creator>Thomas</dc:creator>
		<pubDate>Fri, 13 Apr 2007 19:03:17 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109262</guid>
		<description>(my first attempt to submit this has been rejected as spam, so here is another try ...)

christian: I understand that you need a solution with a cmp function, but I want to share the result of my experiments anyway.

The code I posted above was indeed not intended to be runtime optimal, it was just "the simplest thing that could possible work", for me at last.

I did some measurements and awesome_sort is indeed MUCH faster:

naive_sort 1500 keys =&#62; 0.341000080109 sec
naive_sort 3000 keys =&#62; 1.31200003624 sec
naive_sort 4500 keys =&#62; 3.28399991989 sec
naive_sort 6000 keys =&#62; 5.34800004959 sec

awesome_sort 1500 keys =&#62; 0.00999999046326 sec
awesome_sort 3000 keys =&#62; 0.0699999332428 sec
awesome_sort 4500 keys =&#62; 0.130000114441 sec
awesome_sort 6000 keys =&#62; 0.210999965668 sec

(naive_sort is my solution from above)

But it can be even further improved.

The first thing I noticed is the use of sum to flatten a list.
sum(list_of_lists, []) is analogous to

result = []
for lst in list_of_lists:
    result = result + lst

Given that list1 + list2 is O(len(list1) + len(list2)) it follows that this sum idiom is O(N**2).
In order to replace it, I finally removed restore_natural_order completely and used the inline loop shown below instead.


def awesome_sort2(keys, specific_order):
    mapping = field_mapping2(keys, specific_order)
    # concat the fragments according to the order give by specific_orderS
    result = []
    for key in specific_order:
        result.extend(mapping[key])
    return result


Another opportunity for improvement was the use of pop(0) in loop of field_mapping.
pop(0) has complexity O(N), as the whole array has to be memmove'd, but removing the last list element with pop() is O(1).
So I sorted the list keys in reversed order and used pop() instead of pop(0), leading to:


def field_mapping2(keys, specific_order):
    mapping = dict((k, []) for k in specific_order)
    # change: reverse=True
    sorted_keys, sorted_parents = sorted(keys, reverse=True), sorted(specific_order)
    
    for parent in sorted_parents:
        while sorted_keys and sorted_keys[-1].startswith(parent): # change: -1
            mapping[parent].append(sorted_keys.pop()) # change: pop()
    
    return mapping


All this results in a massive speedup:

awesome_sort 7500 keys =&#62; 0.351000070572 sec
awesome_sort 15000 keys =&#62; 1.29200005531 sec
awesome_sort 22500 keys =&#62; 4.03499984741 sec
awesome_sort 30000 keys =&#62; 11.1059999466 sec

awesome_sort2 7500 keys =&#62; 0.0400002002716 sec
awesome_sort2 15000 keys =&#62; 0.110999822617 sec
awesome_sort2 22500 keys =&#62; 0.120000123978 sec
awesome_sort2 30000 keys =&#62; 0.170000076294 sec</description>
		<content:encoded><![CDATA[<p>(my first attempt to submit this has been rejected as spam, so here is another try &#8230;)</p>
<p>christian: I understand that you need a solution with a cmp function, but I want to share the result of my experiments anyway.</p>
<p>The code I posted above was indeed not intended to be runtime optimal, it was just &#8220;the simplest thing that could possible work&#8221;, for me at last.</p>
<p>I did some measurements and awesome_sort is indeed MUCH faster:</p>
<p>naive_sort 1500 keys =&gt; 0.341000080109 sec<br />
naive_sort 3000 keys =&gt; 1.31200003624 sec<br />
naive_sort 4500 keys =&gt; 3.28399991989 sec<br />
naive_sort 6000 keys =&gt; 5.34800004959 sec</p>
<p>awesome_sort 1500 keys =&gt; 0.00999999046326 sec<br />
awesome_sort 3000 keys =&gt; 0.0699999332428 sec<br />
awesome_sort 4500 keys =&gt; 0.130000114441 sec<br />
awesome_sort 6000 keys =&gt; 0.210999965668 sec</p>
<p>(naive_sort is my solution from above)</p>
<p>But it can be even further improved.</p>
<p>The first thing I noticed is the use of sum to flatten a list.<br />
sum(list_of_lists, []) is analogous to</p>
<p>result = []<br />
for lst in list_of_lists:<br />
    result = result + lst</p>
<p>Given that list1 + list2 is O(len(list1) + len(list2)) it follows that this sum idiom is O(N**2).<br />
In order to replace it, I finally removed restore_natural_order completely and used the inline loop shown below instead.</p>
<p>def awesome_sort2(keys, specific_order):<br />
    mapping = field_mapping2(keys, specific_order)<br />
    # concat the fragments according to the order give by specific_orderS<br />
    result = []<br />
    for key in specific_order:<br />
        result.extend(mapping[key])<br />
    return result</p>
<p>Another opportunity for improvement was the use of pop(0) in loop of field_mapping.<br />
pop(0) has complexity O(N), as the whole array has to be memmove&#8217;d, but removing the last list element with pop() is O(1).<br />
So I sorted the list keys in reversed order and used pop() instead of pop(0), leading to:</p>
<p>def field_mapping2(keys, specific_order):<br />
    mapping = dict((k, []) for k in specific_order)<br />
    # change: reverse=True<br />
    sorted_keys, sorted_parents = sorted(keys, reverse=True), sorted(specific_order)</p>
<p>    for parent in sorted_parents:<br />
        while sorted_keys and sorted_keys[-1].startswith(parent): # change: -1<br />
            mapping[parent].append(sorted_keys.pop()) # change: pop()</p>
<p>    return mapping</p>
<p>All this results in a massive speedup:</p>
<p>awesome_sort 7500 keys =&gt; 0.351000070572 sec<br />
awesome_sort 15000 keys =&gt; 1.29200005531 sec<br />
awesome_sort 22500 keys =&gt; 4.03499984741 sec<br />
awesome_sort 30000 keys =&gt; 11.1059999466 sec</p>
<p>awesome_sort2 7500 keys =&gt; 0.0400002002716 sec<br />
awesome_sort2 15000 keys =&gt; 0.110999822617 sec<br />
awesome_sort2 22500 keys =&gt; 0.120000123978 sec<br />
awesome_sort2 30000 keys =&gt; 0.170000076294 sec</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: christian</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109172</link>
		<dc:creator>christian</dc:creator>
		<pubDate>Fri, 13 Apr 2007 01:28:11 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109172</guid>
		<description>Thanks for more ideas guys!

Gary:  Very cool.  Nice comments too :-)  I think I might go with something like this.  I'll have to massage it into a cmp-like function to work with the API that I am using though.  It requires a cmp function to sort the fields.

sri: Also a good looking solution, and you are right, there is definite merging going on.  However, as Gary mentioned, assuming the underscores probably wouldn't work.  A parent name could be foo_bar and a derived name could be foo_bar_spam_eggs.  Crazy, I know :-)

And JR, well, thanks for sticking around :-)</description>
		<content:encoded><![CDATA[<p>Thanks for more ideas guys!</p>
<p>Gary:  Very cool.  Nice comments too <img src='http://blog.dowski.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  I think I might go with something like this.  I&#8217;ll have to massage it into a cmp-like function to work with the API that I am using though.  It requires a cmp function to sort the fields.</p>
<p>sri: Also a good looking solution, and you are right, there is definite merging going on.  However, as Gary mentioned, assuming the underscores probably wouldn&#8217;t work.  A parent name could be foo_bar and a derived name could be foo_bar_spam_eggs.  Crazy, I know <img src='http://blog.dowski.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>And JR, well, thanks for sticking around <img src='http://blog.dowski.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /></p>
]]></content:encoded>
	</item>
	<item>
		<title>By: JR Rozko</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109166</link>
		<dc:creator>JR Rozko</dc:creator>
		<pubDate>Thu, 12 Apr 2007 23:53:17 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109166</guid>
		<description>ok, I guess they have some good ideas as well.</description>
		<content:encoded><![CDATA[<p>ok, I guess they have some good ideas as well.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: sri</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109163</link>
		<dc:creator>sri</dc:creator>
		<pubDate>Thu, 12 Apr 2007 23:43:14 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109163</guid>
		<description>well, he did say *derived* keys,
so you should be able to exploit on that</description>
		<content:encoded><![CDATA[<p>well, he did say *derived* keys,<br />
so you should be able to exploit on that</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Gary Bernhardt</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109157</link>
		<dc:creator>Gary Bernhardt</dc:creator>
		<pubDate>Thu, 12 Apr 2007 23:18:41 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109157</guid>
		<description>sri,

I thought about doing it that way as well, but Christian never said you could assume underscores.  Without a token to split on, you'd have to search for common prefixes between the two sets of keys.  In that case, I think you'd either have to loop over both lists (n^2), or sort both of them and process them both in order (something like my solution - n log n).</description>
		<content:encoded><![CDATA[<p>sri,</p>
<p>I thought about doing it that way as well, but Christian never said you could assume underscores.  Without a token to split on, you&#8217;d have to search for common prefixes between the two sets of keys.  In that case, I think you&#8217;d either have to loop over both lists (n^2), or sort both of them and process them both in order (something like my solution - n log n).</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Gary Bernhardt</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109145</link>
		<dc:creator>Gary Bernhardt</dc:creator>
		<pubDate>Thu, 12 Apr 2007 21:41:01 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109145</guid>
		<description>I *think* this is O(n log n) (and only then, because of the original sorting of the keys).  It builds a mapping from the parent keys (those in the original class) to the child keys (all of them).  It does that by sorting both the parents and the children and stepping through them at the same time, so it's O(n).  Then it massages the child lists to get them back into their original order, and finally joins all of the child lists together based on the parents' specific_order.

This started out so simple, and became so disgusting.  I promise, I don't usually write code this nasty. :)

&lt;pre&gt;
def awesome_sort(keys):
    mapping = field_mapping(keys)
    mapping = restore_natural_order(mapping, keys)
    # Join the child fields, ordered by their specific order
    return sum([mapping[k] for k in specific_order], [])

def field_mapping(keys):
    """
    Return a dictionary mapping each parent field to all fields that start with
    it (including itself).
    """
    mapping = dict((k, []) for k in specific_order)
    # Sort both the keys and the parent keys so we can step through them
    # together.
    sorted_keys, sorted_parents = sorted(keys), sorted(specific_order)

    # This loop is O(n) - it hits each element in sorted_keys and
    # sorted_parents at most once.
    for parent in sorted_parents:
        # Eat any keys that begin with this parent key
        while sorted_keys and sorted_keys[0].startswith(parent):
            mapping[parent].append(sorted_keys.pop(0))

    return mapping

def restore_natural_order(mapping, keys):
    """
    Given a mapping from field_mapping(), reorder the child key lists to their
    original order.
    """
    # Invert the mapping - build a dictionary mapping each child key to its
    # parent key.
    inverse_mapping = {}
    for parent, child_keys in mapping.iteritems():
        for k in child_keys:
            inverse_mapping[k] = parent

    # Invert the inverted mapping (ugh) to rebuild the original mapping, but
    # with the original order.
    result = dict((k, []) for k in specific_order)
    for key in keys:
        parent = inverse_mapping[key]
        result[parent].append(key)

    return result

assert sorted(keys, cmp_headers) == awesome_sort(keys)
&lt;/pre&gt;
&lt;em&gt;EDIT: added PRE tags back in - cw&lt;/em&gt;</description>
		<content:encoded><![CDATA[<p>I *think* this is O(n log n) (and only then, because of the original sorting of the keys).  It builds a mapping from the parent keys (those in the original class) to the child keys (all of them).  It does that by sorting both the parents and the children and stepping through them at the same time, so it&#8217;s O(n).  Then it massages the child lists to get them back into their original order, and finally joins all of the child lists together based on the parents&#8217; specific_order.</p>
<p>This started out so simple, and became so disgusting.  I promise, I don&#8217;t usually write code this nasty. <img src='http://blog.dowski.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<pre>
def awesome_sort(keys):
    mapping = field_mapping(keys)
    mapping = restore_natural_order(mapping, keys)
    # Join the child fields, ordered by their specific order
    return sum([mapping[k] for k in specific_order], [])

def field_mapping(keys):
    &#8220;&#8221;"
    Return a dictionary mapping each parent field to all fields that start with
    it (including itself).
    &#8220;&#8221;"
    mapping = dict((k, []) for k in specific_order)
    # Sort both the keys and the parent keys so we can step through them
    # together.
    sorted_keys, sorted_parents = sorted(keys), sorted(specific_order)

    # This loop is O(n) - it hits each element in sorted_keys and
    # sorted_parents at most once.
    for parent in sorted_parents:
        # Eat any keys that begin with this parent key
        while sorted_keys and sorted_keys[0].startswith(parent):
            mapping[parent].append(sorted_keys.pop(0))

    return mapping

def restore_natural_order(mapping, keys):
    &#8220;&#8221;"
    Given a mapping from field_mapping(), reorder the child key lists to their
    original order.
    &#8220;&#8221;"
    # Invert the mapping - build a dictionary mapping each child key to its
    # parent key.
    inverse_mapping = {}
    for parent, child_keys in mapping.iteritems():
        for k in child_keys:
            inverse_mapping[k] = parent

    # Invert the inverted mapping (ugh) to rebuild the original mapping, but
    # with the original order.
    result = dict((k, []) for k in specific_order)
    for key in keys:
        parent = inverse_mapping[key]
        result[parent].append(key)

    return result

assert sorted(keys, cmp_headers) == awesome_sort(keys)
</pre>
<p><em>EDIT: added PRE tags back in - cw</em></p>
]]></content:encoded>
	</item>
	<item>
		<title>By: sri</title>
		<link>http://blog.dowski.com/2007/04/12/a-sorting-problem/#comment-109137</link>
		<dc:creator>sri</dc:creator>
		<pubDate>Thu, 12 Apr 2007 20:30:11 +0000</pubDate>
		<guid isPermaLink="false">http://blog.dowski.com/?p=201#comment-109137</guid>
		<description>OK, screwed up twice:
here is in on my blog:
http://defcraft.blogspot.com/2007/04/merge-problem.html</description>
		<content:encoded><![CDATA[<p>OK, screwed up twice:<br />
here is in on my blog:<br />
<a href="http://defcraft.blogspot.com/2007/04/merge-problem.html" rel="nofollow">http://defcraft.blogspot.com/2007/04/merge-problem.html</a></p>
]]></content:encoded>
	</item>
</channel>
</rss>
