'inverting' a dict

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Irmen de Jong

    'inverting' a dict

    Hi
    I have this dict that maps a name to a sequence of other names.
    I want to have it reversed, i.e., map the other names each to
    the key they belong to (yes, the other names are unique and
    they only occur once). Like this:

    { "key1": ("value1", "value2"), "key2": ("value3,) }

    -->

    { "value1": "key1", "value2": "key1", "value3": "key2" }

    What I'm doing is using a nested loop:

    dict2={}
    for (key,value) in dict1.items():
    for name in value:
    dict2[name] = key

    which is simple enough, but I'm hearing this little voice in
    the back of my head saying "there's a simpler solution".
    Is there? What is it? ;-)

    Thanks
    --Irmen.

  • anton muhin

    #2
    Re: 'inverting' a dict

    Irmen de Jong wrote:[color=blue]
    > Hi
    > I have this dict that maps a name to a sequence of other names.
    > I want to have it reversed, i.e., map the other names each to
    > the key they belong to (yes, the other names are unique and
    > they only occur once). Like this:
    >
    > { "key1": ("value1", "value2"), "key2": ("value3,) }
    >
    > -->
    >
    > { "value1": "key1", "value2": "key1", "value3": "key2" }
    >
    > What I'm doing is using a nested loop:
    >
    > dict2={}
    > for (key,value) in dict1.items():
    > for name in value:
    > dict2[name] = key
    >
    > which is simple enough, but I'm hearing this little voice in
    > the back of my head saying "there's a simpler solution".
    > Is there? What is it? ;-)
    >
    > Thanks
    > --Irmen.
    >[/color]

    It's seems like rather nice Python for me ;). Two more variants:

    inverted = {}
    for k, vs in d.iteritems():
    inverted.update (dict([(v, k) for v in vs]))

    map(
    lambda (k, vs): inverted.update (dict([(v, k) for v in vs])),
    d.iteritems()
    )

    happy new year,
    anton.

    Comment

    • engsolnom@ipns.com

      #3
      Re: 'inverting' a dict

      On Tue, 30 Dec 2003 18:30:56 +0100, Irmen de Jong
      <irmen@-NOSPAM-REMOVETHIS-xs4all.nl> wrote:
      [color=blue]
      >Hi
      >I have this dict that maps a name to a sequence of other names.
      >I want to have it reversed, i.e., map the other names each to
      >the key they belong to (yes, the other names are unique and
      >they only occur once). Like this:
      >
      >{ "key1": ("value1", "value2"), "key2": ("value3,) }
      >
      >-->
      >
      >{ "value1": "key1", "value2": "key1", "value3": "key2" }
      >
      >What I'm doing is using a nested loop:
      >
      >dict2={}
      >for (key,value) in dict1.items():
      > for name in value:
      > dict2[name] = key
      >
      >which is simple enough, but I'm hearing this little voice in
      >the back of my head saying "there's a simpler solution".
      >Is there? What is it? ;-)
      >
      >Thanks
      >--Irmen.[/color]

      I had a need for a 'reverse' dictionary also. This is how I approached
      it:

      source_dict = {'A': 1,'B': 2,'C': 3,'D': 4}
      target_dict = {}
      for item in source_dict.ite ritems():
      target_dict[item[1]] = item[0]

      Or, a bit more contrived....

      source_dict = {'A': [[1,2,3],100],'B': [[4,5,6],200],'C':
      [[7,8,9],300]}
      target_dict = {}
      for item in source_dict.ite ritems():
      target_dict[item[1][1]] = item[0]

      Disclaimer: I'm a newbie
      Norm


      Comment

      • Yermat

        #4
        Re: 'inverting' a dict

        or even shorter[color=blue][color=green][color=darkred]
        >>> d = {'key1' : ('value1','valu e2'), 'key2': ('value3',) }[/color][/color][/color]
        [color=blue][color=green][color=darkred]
        >>> dict([(v,k) for k in d.iterkeys() for v in d[k]])[/color][/color][/color]
        {'value3': 'key2', 'value2': 'key1', 'value1': 'key1'}


        "anton muhin" <antonmuhin@ram bler.ru> a écrit dans le message de
        news:bssgf2$10n t3$1@ID-217427.news.uni-berlin.de[color=blue]
        > Irmen de Jong wrote:[color=green]
        >> Hi
        >> I have this dict that maps a name to a sequence of other names.
        >> I want to have it reversed, i.e., map the other names each to
        >> the key they belong to (yes, the other names are unique and
        >> they only occur once). Like this:
        >>
        >> { "key1": ("value1", "value2"), "key2": ("value3,) }
        >>
        >> -->
        >>
        >> { "value1": "key1", "value2": "key1", "value3": "key2" }
        >>
        >> What I'm doing is using a nested loop:
        >>
        >> dict2={}
        >> for (key,value) in dict1.items():
        >> for name in value:
        >> dict2[name] = key
        >>
        >> which is simple enough, but I'm hearing this little voice in
        >> the back of my head saying "there's a simpler solution".
        >> Is there? What is it? ;-)
        >>
        >> Thanks
        >> --Irmen.
        >>[/color]
        >
        > It's seems like rather nice Python for me ;). Two more variants:
        >
        > inverted = {}
        > for k, vs in d.iteritems():
        > inverted.update (dict([(v, k) for v in vs]))
        >
        > map(
        > lambda (k, vs): inverted.update (dict([(v, k) for v in vs])),
        > d.iteritems()
        > )
        >
        > happy new year,
        > anton.[/color]


        Comment

        • Peter Otten

          #5
          Re: 'inverting' a dict

          Irmen de Jong wrote:
          [color=blue]
          > I have this dict that maps a name to a sequence of other names.
          > I want to have it reversed, i.e., map the other names each to
          > the key they belong to (yes, the other names are unique and
          > they only occur once). Like this:[/color]

          [...]
          [color=blue]
          > What I'm doing is using a nested loop:
          >
          > dict2={}
          > for (key,value) in dict1.items():
          > for name in value:
          > dict2[name] = key
          >
          > which is simple enough, but I'm hearing this little voice in
          > the back of my head saying "there's a simpler solution".
          > Is there? What is it? ;-)[/color]

          Here's what I've come up with:

          import itertools

          original = {"key1": ("value1", "value2"), "key2": ("value3",)}

          def forInv(original ):
          result = {}
          for (key, values) in original.iterit ems():
          for val in values:
          result[val] = key
          return result

          def updateInv(origi nal):
          result = {}
          for (key, values) in original.iterit ems():
          result.update(d ict.fromkeys(va lues, key))
          return result

          def iterInv(origina l):
          result = {}
          for (key, values) in original.iterit ems():
          result.update(d ict(itertools.i zip(values, itertools.repea t(key))))
          return result

          def iterInv2(origin al):
          return dict(itertools. chain(*[itertools.izip( values,
          itertools.repea t(key))
          for key, values in original.iterit ems()]))

          def compInv(origina l):
          return dict([(val, key) for (key, values) in original.iterit ems() for
          val in values])

          wanted = { "value1": "key1", "value2": "key1", "value3": "key2" }

          for inv in globals().value s():
          if callable(inv):
          print inv.__name__,
          if inv(original) == wanted:
          print "OK"
          else:
          print "FAILED"

          Conclusion: my favourite toys, itertools and list comprehensions, lead to
          clumsier code - well, me at least. So I would recommend that you don't
          listen to that voice.

          Peter

          Comment

          • Jeff Epler

            #6
            Re: 'inverting' a dict

            Does this listcomp give the right result?
            dict([(v, k) for k, vs in original.iterit ems() for v in vs])

            Jeff

            Comment

            • Wade Leftwich

              #7
              Re: 'inverting' a dict

              Irmen de Jong <irmen@-NOSPAM-REMOVETHIS-xs4all.nl> wrote in message news:<3ff1b688$ 0$319$e4fe514c@ news.xs4all.nl> ...[color=blue]
              > Hi
              > I have this dict that maps a name to a sequence of other names.
              > I want to have it reversed, i.e., map the other names each to
              > the key they belong to (yes, the other names are unique and
              > they only occur once). Like this:
              >
              > { "key1": ("value1", "value2"), "key2": ("value3,) }
              >
              > -->
              >
              > { "value1": "key1", "value2": "key1", "value3": "key2" }
              >
              > What I'm doing is using a nested loop:
              >
              > dict2={}
              > for (key,value) in dict1.items():
              > for name in value:
              > dict2[name] = key
              >
              > which is simple enough, but I'm hearing this little voice in
              > the back of my head saying "there's a simpler solution".
              > Is there? What is it? ;-)
              >
              > Thanks
              > --Irmen.[/color]


              def invert_dict(D):
              return dict([(y,x) for (x,y) in D.items()])

              -- Wade Leftwich
              Ithaca, NY

              Comment

              • Irmen de Jong

                #8
                Re: 'inverting' a dict

                Yermat wrote:
                [color=blue]
                > or even shorter
                >[color=green][color=darkred]
                >>>>d = {'key1' : ('value1','valu e2'), 'key2': ('value3',) }[/color][/color]
                >
                >[color=green][color=darkred]
                >>>>dict([(v,k) for k in d.iterkeys() for v in d[k]])[/color][/color]
                >
                > {'value3': 'key2', 'value2': 'key1', 'value1': 'key1'}[/color]

                Yep, thanks, that must have been the solution that I was looking for!
                I understand list comprehensions, but keep forgetting about the
                multiple-loop-kind.

                --Irmen.

                Comment

                • Irmen de Jong

                  #9
                  Re: 'inverting' a dict

                  Jeff Epler wrote:[color=blue]
                  > Does this listcomp give the right result?
                  > dict([(v, k) for k, vs in original.iterit ems() for v in vs])[/color]

                  Yes it does, and it's even more concise than Yermat's solution

                  dict([(v,k) for k in d.iterkeys() for v in d[k]])

                  because it avoids a dict lookup every loop cycle. Thanks Jeff!

                  --Irmen

                  Comment

                  • Irmen de Jong

                    #10
                    Re: 'inverting' a dict

                    Wade Leftwich wrote:
                    [color=blue]
                    > def invert_dict(D):
                    > return dict([(y,x) for (x,y) in D.items()])[/color]

                    No, this one only works for non-sequence-type values.
                    I wanted to map every item of a value (a sequence)
                    to the corresponding key.

                    --Irmen

                    Comment

                    • Elaine Jackson

                      #11
                      Re: 'inverting' a dict

                      dict2=dict([(a,b) for b in dict1.keys() for a in dict1[b]])

                      HTH


                      "Irmen de Jong" <irmen@-NOSPAM-REMOVETHIS-xs4all.nl> wrote in message
                      news:3ff1b688$0 $319$e4fe514c@n ews.xs4all.nl.. .
                      | Hi
                      | I have this dict that maps a name to a sequence of other names.
                      | I want to have it reversed, i.e., map the other names each to
                      | the key they belong to (yes, the other names are unique and
                      | they only occur once). Like this:
                      |
                      | { "key1": ("value1", "value2"), "key2": ("value3,) }
                      |
                      | -->
                      |
                      | { "value1": "key1", "value2": "key1", "value3": "key2" }
                      |
                      | What I'm doing is using a nested loop:
                      |
                      | dict2={}
                      | for (key,value) in dict1.items():
                      | for name in value:
                      | dict2[name] = key
                      |
                      | which is simple enough, but I'm hearing this little voice in
                      | the back of my head saying "there's a simpler solution".
                      | Is there? What is it? ;-)
                      |
                      | Thanks
                      | --Irmen.
                      |


                      Comment

                      • Bob van der Poel

                        #12
                        Re: 'inverting' a dict


                        This is sort of off topic for the thread, but I've got a similar
                        problem. In this case I have a dict like:

                        { 'key1': 'value1', 'key2': value2}

                        and I sometimes need to find the key for the value. All values/keys are
                        unique. I just use a loop:

                        for a in dict:
                        if dict[a]== targ:
                        return a
                        return None

                        But it'd be nice to have something faster, etc.


                        Elaine Jackson wrote:[color=blue]
                        > dict2=dict([(a,b) for b in dict1.keys() for a in dict1[b]])
                        >
                        > HTH
                        >
                        >
                        > "Irmen de Jong" <irmen@-NOSPAM-REMOVETHIS-xs4all.nl> wrote in message
                        > news:3ff1b688$0 $319$e4fe514c@n ews.xs4all.nl.. .
                        > | Hi
                        > | I have this dict that maps a name to a sequence of other names.
                        > | I want to have it reversed, i.e., map the other names each to
                        > | the key they belong to (yes, the other names are unique and
                        > | they only occur once). Like this:
                        > |
                        > | { "key1": ("value1", "value2"), "key2": ("value3,) }
                        > |
                        > | -->
                        > |
                        > | { "value1": "key1", "value2": "key1", "value3": "key2" }
                        > |
                        > | What I'm doing is using a nested loop:
                        > |
                        > | dict2={}
                        > | for (key,value) in dict1.items():
                        > | for name in value:
                        > | dict2[name] = key
                        > |
                        > | which is simple enough, but I'm hearing this little voice in
                        > | the back of my head saying "there's a simpler solution".
                        > | Is there? What is it? ;-)
                        > |
                        > | Thanks
                        > | --Irmen.
                        > |
                        >
                        >[/color]

                        --
                        Bob van der Poel ** Wynndel, British Columbia, CANADA **
                        EMAIL: bvdpoel@kootena y.com
                        WWW: http://www.kootenay.com/~bvdpoel

                        Comment

                        • Raymond Hettinger

                          #13
                          Re: 'inverting' a dict

                          Irmen de Jong <irmen@-NOSPAM-REMOVETHIS-xs4all.nl> wrote in message news:<3ff2cba6$ 0$316$e4fe514c@ news.xs4all.nl> ...[color=blue]
                          > Wade Leftwich wrote:
                          >[color=green]
                          > > def invert_dict(D):
                          > > return dict([(y,x) for (x,y) in D.items()])[/color]
                          >
                          > No, this one only works for non-sequence-type values.
                          > I wanted to map every item of a value (a sequence)
                          > to the corresponding key.
                          >
                          > --Irmen[/color]

                          Try a nested list comprehension:
                          [color=blue][color=green][color=darkred]
                          >>> data = { "key1": ("value1", "value2"), "key2": ("value3",) }
                          >>> dict([(v,k) for k,vlist in data.iteritems( ) for v in vlist])[/color][/color][/color]
                          {'value3': 'key2', 'value2': 'key1', 'value1': 'key1'}


                          Raymond Hettinger

                          P.S. In Py2.4, it will be possible to write this without the
                          brackets. The resulting generator expression is faster and more
                          memory friendly:
                          [color=blue][color=green][color=darkred]
                          >>> dict((v,k) for k,vlist in data.iteritems( ) for v in vlist)[/color][/color][/color]
                          {'value3': 'key2', 'value2': 'key1', 'value1': 'key1'}

                          Comment

                          • Peter Abel

                            #14
                            Re: 'inverting' a dict

                            Irmen de Jong <irmen@-NOSPAM-REMOVETHIS-xs4all.nl> wrote in message news:<3ff1b688$ 0$319$e4fe514c@ news.xs4all.nl> ...[color=blue]
                            > Hi
                            > I have this dict that maps a name to a sequence of other names.
                            > I want to have it reversed, i.e., map the other names each to
                            > the key they belong to (yes, the other names are unique and
                            > they only occur once). Like this:
                            >
                            > { "key1": ("value1", "value2"), "key2": ("value3,) }
                            >
                            > -->
                            >
                            > { "value1": "key1", "value2": "key1", "value3": "key2" }
                            >
                            > What I'm doing is using a nested loop:
                            >
                            > dict2={}
                            > for (key,value) in dict1.items():
                            > for name in value:
                            > dict2[name] = key
                            >
                            > which is simple enough, but I'm hearing this little voice in
                            > the back of my head saying "there's a simpler solution".
                            > Is there? What is it? ;-)
                            >
                            > Thanks
                            > --Irmen.[/color]

                            .... and after all there is always a hacky one-liner:[color=blue][color=green][color=darkred]
                            >>> org = {"key1": ("value1", "value2"), "key2": ("value3",)}
                            >>> dict(reduce(lam bda l,(k,v):l.exten d(zip(v,(k,)*le n(v))) or l,org.items(),[]))[/color][/color][/color]
                            {'value3': 'key2', 'value2': 'key1', 'value1': 'key1'}[color=blue][color=green][color=darkred]
                            >>>[/color][/color][/color]

                            Happy new year
                            Peter

                            Comment

                            • sdd

                              #15
                              Re: 'inverting' a dict

                              Bob van der Poel wrote:[color=blue]
                              > This is sort of off topic for the thread, but I've got a similar
                              > problem. In this case I have a dict like:
                              >
                              > { 'key1': 'value1', 'key2': value2}
                              >
                              > and I sometimes need to find the key for the value. All values/keys are
                              > unique. I just use a loop:
                              >
                              > for a in dict:
                              > if dict[a]== targ:
                              > return a
                              > return None[/color]
                              For a few hunts, I'd do:
                              for key,value in dictionary.iter items():
                              if value == target:
                              return key
                              return None

                              Of course, if it probed a lot between changes, it's better to

                              reversedictiona ry = dict([(v,k) for k,v in dictionary.item s()])

                              -Scott David Daniels
                              Scott.Daniels@A cm.Org

                              Comment

                              Working...