#! /usr/bin/perl -w use strict; my %replace = ( qw/ COM SEC MOB HIS MOC COM ICM1 INO ICM2 INO EU HIS CY GRE AE MOB IN ICC GR GRE MH MOC CO MOC MO HIS /); my @order; demangle( \%replace, \@order ); sub demangle { my $r = shift; my $order = shift; my %invert; @invert{ values %$r } = keys %$r; my( %okay, %collide ); for my $key( sort keys %$r ) { if( exists $invert{$key} ) { $collide{$key} = $r->{$key}; } else { $okay{$key} = $r->{$key}; } } unshift @$order, \%okay; if( %collide ) { my @loop_keys = sort keys %collide; my @loop_vals = sort values %collide; my $is_loop = 1; for( my $n = 0; $n < scalar @loop_keys; ++$n ) { if( $loop_keys[$n] ne $loop_vals[$n] ) { $is_loop = 0; last; } } if( $is_loop ) { warn "\t$_\t$collide{$_}\n" for sort keys %collide; die "loop in transforms detected, bailing out\n"; } demangle( \%collide, $order ); } } my $pass = 0; for my $r( @order ) { ++$pass; print "Pass $pass\n"; for my $key( keys %$r ) { print "\t$key -> $r->{$key}\n"; } } __END__ # produces: Pass 1 COM -> SEC Pass 2 MOB -> HIS MOC -> COM Pass 3 ICM1 -> INO ICM2 -> INO EU -> HIS CY -> GRE IN -> ICC GR -> GRE MH -> MOC CO -> MOC AE -> MOB MO -> HIS