Inverting an associative array












1















Let's say I have an associative array in bash,



declare -A hash
hash=(
["foo"]=aa
["bar"]=bb
["baz"]=aa
["quux"]=bb
["wibble"]=cc
["wobble"]=aa
)


where both keys and values are unknown to me (the actual data is read from external sources).



How may I create an array of the keys corresponding to the same value, so that I may, in a loop over all unique values, do



printf 'Value "%s" is present with the following keys: %sn' "$value" "${keys[*]}"


and get the output (not necessarily in this order)



Value "aa" is present with the following keys: foo baz wobble
Value "bb" is present with the following keys: bar quux
Value "cc" is present with the following keys: wibble


The important bit is that the keys are stored as separate elements in the keys array and that they therefore do not need to be parsed out of a text string.



I could do something like



declare -A seen
seen=()
for value in "${hash[@]}"; do
if [ -n "${seen[$value]}" ]; then
continue
fi

keys=()
for key in "${!hash[@]}"; do
if [ "${hash[$key]}" = "$value" ]; then
keys+=( "$key" )
fi
done

printf 'Value "%s" is present with the following keys: %sn'
"$value" "${keys[*]}"

seen[$value]=1
done


But it seems a bit inefficient with that double loop.



Is there a piece of array syntax that I've missed for bash?



Would doing this in e.g. zsh give me access to more powerful array manipulation tools?



In Perl, I would do



my %hash = (
'foo' => 'aa',
'bar' => 'bb',
'baz' => 'aa',
'quux' => 'bb',
'wibble' => 'cc',
'wobble' => 'aa'
);

my %keys;
while ( my ( $key, $value ) = each(%hash) ) {
push( @{ $keys{$value} }, $key );
}

foreach my $value ( keys(%keys) ) {
printf( "Value "%s" is present with the following keys: %sn",
$value, join( " ", @{ $keys{$value} } ) );
}


But bash associative arrays can't hold arrays...



I'd also be interested in any old school solution possibly using some form of indirect indexing (building a set of index array(s) when reading the values that I said I had in hash above?). It feels like there ought to be a way to do this in linear time.










share|improve this question





























    1















    Let's say I have an associative array in bash,



    declare -A hash
    hash=(
    ["foo"]=aa
    ["bar"]=bb
    ["baz"]=aa
    ["quux"]=bb
    ["wibble"]=cc
    ["wobble"]=aa
    )


    where both keys and values are unknown to me (the actual data is read from external sources).



    How may I create an array of the keys corresponding to the same value, so that I may, in a loop over all unique values, do



    printf 'Value "%s" is present with the following keys: %sn' "$value" "${keys[*]}"


    and get the output (not necessarily in this order)



    Value "aa" is present with the following keys: foo baz wobble
    Value "bb" is present with the following keys: bar quux
    Value "cc" is present with the following keys: wibble


    The important bit is that the keys are stored as separate elements in the keys array and that they therefore do not need to be parsed out of a text string.



    I could do something like



    declare -A seen
    seen=()
    for value in "${hash[@]}"; do
    if [ -n "${seen[$value]}" ]; then
    continue
    fi

    keys=()
    for key in "${!hash[@]}"; do
    if [ "${hash[$key]}" = "$value" ]; then
    keys+=( "$key" )
    fi
    done

    printf 'Value "%s" is present with the following keys: %sn'
    "$value" "${keys[*]}"

    seen[$value]=1
    done


    But it seems a bit inefficient with that double loop.



    Is there a piece of array syntax that I've missed for bash?



    Would doing this in e.g. zsh give me access to more powerful array manipulation tools?



    In Perl, I would do



    my %hash = (
    'foo' => 'aa',
    'bar' => 'bb',
    'baz' => 'aa',
    'quux' => 'bb',
    'wibble' => 'cc',
    'wobble' => 'aa'
    );

    my %keys;
    while ( my ( $key, $value ) = each(%hash) ) {
    push( @{ $keys{$value} }, $key );
    }

    foreach my $value ( keys(%keys) ) {
    printf( "Value "%s" is present with the following keys: %sn",
    $value, join( " ", @{ $keys{$value} } ) );
    }


    But bash associative arrays can't hold arrays...



    I'd also be interested in any old school solution possibly using some form of indirect indexing (building a set of index array(s) when reading the values that I said I had in hash above?). It feels like there ought to be a way to do this in linear time.










    share|improve this question



























      1












      1








      1








      Let's say I have an associative array in bash,



      declare -A hash
      hash=(
      ["foo"]=aa
      ["bar"]=bb
      ["baz"]=aa
      ["quux"]=bb
      ["wibble"]=cc
      ["wobble"]=aa
      )


      where both keys and values are unknown to me (the actual data is read from external sources).



      How may I create an array of the keys corresponding to the same value, so that I may, in a loop over all unique values, do



      printf 'Value "%s" is present with the following keys: %sn' "$value" "${keys[*]}"


      and get the output (not necessarily in this order)



      Value "aa" is present with the following keys: foo baz wobble
      Value "bb" is present with the following keys: bar quux
      Value "cc" is present with the following keys: wibble


      The important bit is that the keys are stored as separate elements in the keys array and that they therefore do not need to be parsed out of a text string.



      I could do something like



      declare -A seen
      seen=()
      for value in "${hash[@]}"; do
      if [ -n "${seen[$value]}" ]; then
      continue
      fi

      keys=()
      for key in "${!hash[@]}"; do
      if [ "${hash[$key]}" = "$value" ]; then
      keys+=( "$key" )
      fi
      done

      printf 'Value "%s" is present with the following keys: %sn'
      "$value" "${keys[*]}"

      seen[$value]=1
      done


      But it seems a bit inefficient with that double loop.



      Is there a piece of array syntax that I've missed for bash?



      Would doing this in e.g. zsh give me access to more powerful array manipulation tools?



      In Perl, I would do



      my %hash = (
      'foo' => 'aa',
      'bar' => 'bb',
      'baz' => 'aa',
      'quux' => 'bb',
      'wibble' => 'cc',
      'wobble' => 'aa'
      );

      my %keys;
      while ( my ( $key, $value ) = each(%hash) ) {
      push( @{ $keys{$value} }, $key );
      }

      foreach my $value ( keys(%keys) ) {
      printf( "Value "%s" is present with the following keys: %sn",
      $value, join( " ", @{ $keys{$value} } ) );
      }


      But bash associative arrays can't hold arrays...



      I'd also be interested in any old school solution possibly using some form of indirect indexing (building a set of index array(s) when reading the values that I said I had in hash above?). It feels like there ought to be a way to do this in linear time.










      share|improve this question
















      Let's say I have an associative array in bash,



      declare -A hash
      hash=(
      ["foo"]=aa
      ["bar"]=bb
      ["baz"]=aa
      ["quux"]=bb
      ["wibble"]=cc
      ["wobble"]=aa
      )


      where both keys and values are unknown to me (the actual data is read from external sources).



      How may I create an array of the keys corresponding to the same value, so that I may, in a loop over all unique values, do



      printf 'Value "%s" is present with the following keys: %sn' "$value" "${keys[*]}"


      and get the output (not necessarily in this order)



      Value "aa" is present with the following keys: foo baz wobble
      Value "bb" is present with the following keys: bar quux
      Value "cc" is present with the following keys: wibble


      The important bit is that the keys are stored as separate elements in the keys array and that they therefore do not need to be parsed out of a text string.



      I could do something like



      declare -A seen
      seen=()
      for value in "${hash[@]}"; do
      if [ -n "${seen[$value]}" ]; then
      continue
      fi

      keys=()
      for key in "${!hash[@]}"; do
      if [ "${hash[$key]}" = "$value" ]; then
      keys+=( "$key" )
      fi
      done

      printf 'Value "%s" is present with the following keys: %sn'
      "$value" "${keys[*]}"

      seen[$value]=1
      done


      But it seems a bit inefficient with that double loop.



      Is there a piece of array syntax that I've missed for bash?



      Would doing this in e.g. zsh give me access to more powerful array manipulation tools?



      In Perl, I would do



      my %hash = (
      'foo' => 'aa',
      'bar' => 'bb',
      'baz' => 'aa',
      'quux' => 'bb',
      'wibble' => 'cc',
      'wobble' => 'aa'
      );

      my %keys;
      while ( my ( $key, $value ) = each(%hash) ) {
      push( @{ $keys{$value} }, $key );
      }

      foreach my $value ( keys(%keys) ) {
      printf( "Value "%s" is present with the following keys: %sn",
      $value, join( " ", @{ $keys{$value} } ) );
      }


      But bash associative arrays can't hold arrays...



      I'd also be interested in any old school solution possibly using some form of indirect indexing (building a set of index array(s) when reading the values that I said I had in hash above?). It feels like there ought to be a way to do this in linear time.







      bash scripting zsh associative-array






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 59 mins ago







      Kusalananda

















      asked 1 hour ago









      KusalanandaKusalananda

      136k17256424




      136k17256424






















          0






          active

          oldest

          votes











          Your Answer








          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "106"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f506891%2finverting-an-associative-array%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Unix & Linux Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f506891%2finverting-an-associative-array%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          CARDNET

          Boot-repair Failure: Unable to locate package grub-common:i386

          濃尾地震