Codeium Golfide

C, 208 205 175 169 bytes

argv[1] : cation
argv[2] : anion
Charges of ions are taken on stdin.

#define z(b)for(i=*++v;*++i>95;);printf(b>1?*i?"(%s)_%d":"%s_%d":"%s",*v,b);

Retina, 86 80 bytes

Thanks to Neil for saving 6 bytes.



Try it online!

Input is linefeed-separated (test suite uses comma-separation for convenience).



We start by prepending a ( to each molecule. The ^ matches on line beginnings because the m) towards the end of the program sets multiline mode for all preceding stages.


We replace the ^[-+]n part with )_, followed by n copies of 1 (i.e. we convert the charges to unary, dropping the signs).


This stage does three things: it divides both charges by their GCD, converts them back to decimal and swaps them. The GCD can be found quite easily in regex, by matching the longest 1+ that lets us match both charges using only the backreference \1. To divide by this, we make use of Retina's "capture count" feature, which tells us how often a group has been used. So $#2 is the first charge divided by the GCD and $#3 is the second charge divided by the GCD (both in decimal).


We remove _1s from the ends of both parts.


And we drop the parentheses from lines which end in a ) (i.e. those that had a _1 there), as well as lines that only contain a single atom.

Finally, we concatenate the two molecules by dropping the linefeed.

APL (Dyalog), 60 59 61 bytes

+2 since charges must be given signed.

Anonymous infix function. Takes list of ions (anion, cation) as left argument and list of corresponding charges as right argument.


Try it online!

{}∘| function where is left argument and is right argument's magnitude:

∧/⍵ LCM of the charges

⍵÷⍨ divide the charges by that

s← store in s (for subscripts)

'_',∘⍕¨ format (stringify) and prepend underbar to each

()/ replicate each letter of each with the corresponding value from:

  s≠1 Is s different from 1? (gives 1 or 0)

  m← store in m (for multiple)

(),¨ prepend the following respectively to those:

  ⍺{}¨m for each, call this function with ions and m as arguments:

   ⎕D,⎕ADigits followed by uppercase Alphabet

   ⍺∩ intersection of ion and that

    tally the number of characters in that

   1< Is one less than that? (i.e. do we have a multi-element ion?)

   ⍵∧ and do we need multiple of that ion?

   : if so, then:

    ')(',⍺ prepend the string to the ion

    1⌽ cyclically rotate one step to the left (puts ) on the right)


     return the ion unmodified

ϵnlist (flatten)