Compacter la chaîne de requête

Sep 29 2020

introduction

Juste sur Hacker news, John Resig envisage de transformer une requête "foo = 1 & foo = 2 & foo = 3 & blah = a & blah = b" en une qui ressemble à ceci: "foo = 1,2,3 & blah = a, b", https://johnresig.com/blog/search-and-dont-replace/. Il prétend "être 10 lignes plus court que la solution de Mark".

Les chaînes de requête sont constituées de séquences de paires nom-valeur. Les paires nom-valeur se composent d'un nom et d'une valeur, séparés par =. les noms et les valeurs sont éventuellement des séquences vides de caractères alphanumériques. Les paires nom-valeur sont séparées par le caractère & dans la séquence. Les valeurs sont uniques pour chaque nom.

Défi

10 lignes plus courtes que la solution de Mark ne suffisent pas.

  • Lisez une chaîne de requête.
  • Combinez les paires de valeurs de nom avec le même nom en une seule paire de valeurs de nom, avec des valeurs concaténées et séparées par une virgule.
  • Sortez la chaîne de requête combinée, avec les paires nom-valeur dans l'ordre dans lequel les noms sont trouvés de gauche à droite dans la chaîne de requête.

C'est du code golf, les échappatoires standard sont fermées.

Exemple d'entrée et de sortie

Contribution:

foo = 1 & foo = & blah = a & foo = 3 & bar = x & blah = b & = 1 & = 2

Production:

toto = 1,, 3 & bla = a, b & bar = x & = 1,2

Contribution:

toto = bar & toto = toto

Production:

toto = bar, toto

Réponses

4 KevinCruijssen Oct 01 2020 at 13:34

05AB1E , 23 octets

'&¡'=δ¡.¡н}εø€Ù}…,=&vyý

Essayez-le en ligne ou vérifiez tous les cas de test .

Explication:

'&¡                     '# Split the (implicit) input-string on "&"
     δ                   # For each inner string:
   '= ¡                 '#  Split it on "="
       .¡ }              # Group all pairs by:
         н               #  Their first value
           ε             # Map over each group of pairs:
            ø            #  Zip/transpose, swapping rows/columns
             €           #  For both inner lists:
              Ù          #   Uniquify it
               }…,=&     # After the map: push string ",=&"
                    v    # Pop and loop over each character `y`:
                     yý  #  Join the inner-most list of strings with `y` as delimiter
                         # (after the loop, the result is output implicitly)

Essayez-le en ligne avec une sortie étape par étape.

12 Arnauld Sep 29 2020 at 21:32

JavaScript (ES6),  103 99 87  86 octets

1 octet enregistré grâce à @MatthewJensen

s=>Object.values(o=/(\w*=)(\w*)/g,s.replace(o,(s,k,v)=>o[k]=o[k]?o[k]+[,v]:s)).join`&`

Essayez-le en ligne!

Commenté

s =>                  // s = query string
  Object.values(      // get the values of ...
    o =               //   ... the object of this regular expression, which is
      /(\w*=)(\w*)/g, //   re-used to store the keys and values of the query
    s.replace(        //   match each key with the '=' sign and the corresponding
      o,              //   value, using the regular expression defined above
      (s, k, v) =>    //   for each matched string s, key k and value v:
        o[k] =        //     update o[k]:
          o[k] ?      //       if it's already defined:
            o[k] +    //         get the current value
            [, v]     //         append a comma, followed by v
          :           //       else:
            s         //         set it to the entire matched string
                      //         (key, '=', first value)
    )                 //   end of replace()
  ).join`&`           // end of Object.values(); join with '&'
10 lucasreta Sep 29 2020 at 20:43

JavaScript, 263 201 196 194 190 189 188 173 161 octets

q=>{w={};q.split`&`.map(x=>{y=x.split`=`;if(!w[y[0]])w[y[0]]=[];w[y[0]].push(y[1])});z=`${Object.entries(w).map(a=>a=[a[0]+'='+a[1].join`,`]).join`&`}`;return z}

Essayez-le en ligne

7 mschauer Sep 29 2020 at 21:46

Julia 1.0 , 106 octets

f(a,z=split.(split(a,'&'),'='),u=first.(z))=join((i*'='*join(last.(z)[u.==i],',') for i in unique(u)),"&")

Essayez-le en ligne!

7 HyperNeutrino Sep 29 2020 at 20:59

Python 3.8 (pré-version) , 116 octets

lambda s:(a:=[k.split("=")for k in s.split("&")])and"&".join(b+"="+",".join(d for c,d in a if c==b)for b in dict(a))

Essayez-le en ligne!

-46 octets grâce aux ovs

-1 octet grâce à Jonathan Allan (dans Py 3.8 PR, avec le morse)

7 Jonah Sep 29 2020 at 22:11

Rubis , 88 80 76 64 octets

->s{s.scan(/(\w*=)(\w*)/).group_by(&:shift).map{|k,v|k+v*?,}*?&}

Essayez-le en ligne!

-8 octets grâce à ovs pour le pointage je pourrais assigner un lambda à une variable

-12 octets grâce à Dingus!

5 Sisyphus Sep 30 2020 at 01:38

Python 3 , 94 octets

lambda s:'&'.join(k+'='+",".join(v)for k,v in parse_qs(s,1).items())
from urllib.parse import*

Essayez-le en ligne!

Quelques choses:

  • Cela ne fonctionne que parce que Python 3.6+ maintient l'ordre du dictionnaire en fonction du moment de l'insertion. Nous aimerions utiliser Python 2 pour une importation plus courte ( urlparsevs urllib.parse) mais les dictionnaires ne sont pas classés correctement.
  • Autant j'aime les f-strings, k+'='+",".join(v)c'est plus court que f'{k}={",".join(v)}'.
  • Le double .joinressemble à une odeur de golf, mais je ne trouve pas de chemin plus court.

Python 3 , 95 octets

from urllib.parse import*
d=parse_qs(input(),1)
print('&'.join(k+'='+','.join(d[k])for k in d))

Essayez-le en ligne!

5 JonathanAllan Sep 29 2020 at 21:54

Gelée , 28 octets

-2 Merci à Zgarb!

ṣ”&ṣ€”=Ṗ€ĠṢịƲµZQ€j€”,j”=)j”&

Un lien monadique acceptant et produisant une liste de caractères.

Essayez-le en ligne!

Comment?

ṣ”&ṣ€”=Ṗ€ĠṢịƲµZQ€j€”,j”=)j”& - Link: list of characters, S
ṣ”&                          - split at '&'s
   ṣ€”=                      - split each at '='s
                               call that A
            Ʋ                - last four links as a monad - f(A):
       Ṗ€                    -   all but last of each
         Ġ                   -   group indices by their values
          Ṣ                  -   sort (since Ġ orders by the values, not the indices)
           ị                 -   index into (A) (vectorises)
             µ          )    - for each:
              Z              -   transpose
               Q€            -   deduplicate each
                 j€”,        -   join each with ','s
                     j”=     -   join with '='s
                         j”& - join with '&'s
5 SomoKRoceS Sep 29 2020 at 22:13

05AB1E , 62 51 octets

(-11 de @kevin)

'&¡'=δ¡D€нÙÐV_UsvYyнk©Xsèyθ',««Xs®ǝU}X妨}Yζí'=ý'&ý

Essayez-le en ligne!


Mon approche 62:

'&¡ε'=¡}D€нÙ©DgÅ0Usvy¬®skDVXsèyθ',««XsYǝU}X妨}®ζεć'=s««˜}˜'&ý

Explication:

'&¡ε'=¡}D€нÙ©DgÅ0Usvy¬®skDVXsèyθ',««XsYǝU}X妨}®ζεć'=s««˜}˜'&ý
'&¡                                                              split by &
   ε'=¡}                                                         foreach: split by =
        D                                                        duplicate
         €н                                                      foreach: push header (get the keys list)
           Ù                                                     uniquify
            ©                                                    save in register c
             Dg                                                  suplicate and get the length of that list of keys
               Å0                                                create a list of 0's with the length above
                 U                                               save in variable X
                  svy                    }                       for each set of key-value
                     ¨sk                                        find the index of that key in the keys list
                         DV                                      save the index in variable y
                           Xsè                                   get the current value of the element of X at index Y (in X we are keeping the concatenation of the values for key i)
                              yθ                                 extract the tail of the element in this iteration (a value to concatenate)
                                ',««                             concatenate with , in between
                                    XsYǝU                        update X with the new value of the element representing the key
                                          X妨}                  remove tail and head from each element of X (removing the trailing , and leading 0)
                                               ®                 push back the list of keys
                                                ζ                zip (list of keys and list of merged values)
                                                 εć'=s««˜}       foreach element in the zipped list, join with = in between such that the result is "key=values"
                                                          ˜      flat
                                                           '&ý   join with &

Essayez-le en ligne!

5 Neil Sep 29 2020 at 22:41

JavaScript, 79 octets

f=
s=>(s=new URLSearchParams(s)).forEach((v,k)=>s.set(k,s.getAll(k)))||unescape(s)
<input oninput=o.textContent=f(this.value)><pre id=o>

Si les E / S peuvent être des chaînes de requête réelles selon la spécification WHATWG, plutôt que des chaînes non valides qui ressemblent à des chaînes de requête mais ne sont pas correctement encodées en URL, alors 7 octets pourraient être enregistrés en stringifiant le résultat au lieu de le déséchapper.

4 Shaggy Sep 29 2020 at 21:17

JavaScript, 106 104 76 bytes

La solution Retina de Port of Neil , publiée avec autorisation.

f=q=>q==(q=q.replace(/(?<=^|&)((\w*=)[^&]*)(.*?)&\2(\w*)/,`$1,$4$3`))?q:f(q)

Essayez-le en ligne!

Original

Un peu de golf en état d'ébriété sur lequel je suis revenu travailler sobre, mais dans le processus j'ai repéré la solution d'Arnauld et j'ai réalisé que j'étais sur la voie de quelque chose de presque identique, alors j'ai laissé cela tel quel.

q=>Object.keys(o={},q.split`&`.map(p=>o[[k,v]=p.split`=`,k]=[...o[k]||[],v])).map(x=>x+`=`+o[x]).join`&`

Essayez-le en ligne!

3 Neil Sep 30 2020 at 15:23

Retina 0.8.2 , 45 octets

+1`(?<=^|&)((\w*=)[^&]*)(.*?)&\2(\w*)
$1,$4$3

Essayez-le en ligne! Explication: Correspond à plusieurs reprises à la première clé dupliquée et à son premier duplicata et joint la valeur à celle de la clé d'origine.

3 Shaggy Sep 30 2020 at 15:52

Japt , 29 28 27 26 24 octets

q& móÈk¶
ü@bøXÎîÕvÎqÃq&

Essayez-le

Sauvé 2 octets grâce à l'inspiration de Kevin .

(Oh, si seulement Japt avait une méthode juste pour grouper, plutôt que pour trier également, cela pourrait être 19 octets .)

Explnataion

q& móÈk¥\nü@bøXÎîÕvÎqÃq&     :Implicit input of string U
q&                            :Split on "&"
   m                          :Map
    ó                         :  Partition after each character that returns falsey (i.e., an empty string)
     È                        :  When passed through the following function
      k                       :    Remove all characters that appear in
       ¥                      :    Literal "=="
        \n                    :Reassign to U
          ü                   :Group & sort by
           @                  :Passing each X through the following function
            b                 :  First index in U
             ø                :  That contains
              XÎ              :    First element of X
                Ã             :End grouping
                 ®            :Map each Z
                  Õ           :  Transpose
                   v          :  Map first element to
                    Î         :    Get first element
                     q        :  Join resulting array
                      Ã       :End map
                       q&     :Join with "&"

Ou, pour fournir une procédure pas à pas:

Contribution

"foo=1&foo=&blah=a&foo=3&bar=x&blah=b&=1&=2"

Divisé

["foo=1","foo=","blah=a","foo=3","bar=x","blah=b","=1","=2"]

Carte et partition

[["foo=","1"],["foo="],["blah=","a"],["foo=","3"],["bar=","x"],["blah=","b"],["=","1"],["=","2"]]

Grouper et trier

[[["foo=","1"],["foo="],["foo=","3"]],[["blah=","a"],["blah=","b"]],[["bar=","x"]],[["=","1"],["=","2"]]]

Carte et ...

Transposer

[[["foo=","foo=","foo="],["1",null,"3"]],[["blah=","blah="],["a","b"]],[["bar="],["x"]],[["=","="],["1","2"]]]

Mapper le premier élément à son premier élément

[["foo=",["1",null,"3"]],["blah=",["a","b"]],["bar=",["x"]],["=",["1","2"]]]

Joindre

["foo=1,,3","blah=a,b","bar=x","=1,2"]

Joindre

"foo=1,,3&blah=a,b&bar=x&=1,2"
2 Noodle9 Sep 30 2020 at 11:38

Awk , 144 \$\cdots\$144 138 octets

Ajout de 6 octets pour corriger un bogue.
Ajout de 4 octets pour corriger un bug gentiment signalé et résolu par Dominic van Essen .
Sauvegardé 6 octets grâce à Dominic van Essen !!!

BEGIN{RS="&"}{if(!(j=i[$1]++))n[m++]=$1;a[$1][j]=$2}END{for(l in n){k=n[l];o=k"=";for(j in a[k])o=o (j>0?",":"")a[k][j];printf z o;z="&"}}

Essayez-le en ligne!

2 CongChen Sep 30 2020 at 13:48

R , 195 octets

{Z=pryr::f
`/`=Z(a,b,el(regmatches(a,gregexpr(b,a))))
`-`=Z(a,b,paste(a,collapse=b))
Z(S,{L=S/'\\w*='
L=factor(L,unique(L))
U=tapply(S/'=\\w*',L,Z(a,trimws(a,,'=')-','))
paste0(names(U),U)-'&'})}

Essayez-le en ligne!

2 valsaysReinstateMonica Sep 30 2020 at 18:15

Lua , 162 octets

l,t={},{}(...):gsub('(%w-)=(%w-)',load"k,v=...o=t[k]l[#l+1]=not o and k or _ t[k]=o and o..','..v or v")for i=1,#l do io.write(i>1 and'&'or'',l[i],'=',t[l[i]])end

Essayez-le en ligne!

C'est long, hein. Il aurait pu être beaucoup plus court si ce n'était pour commander un requiment.

Explication:

l,t={},{} -- list (by first inclusion), table (keys to string)
-- (ab)use string replacement function to callback over matches in input string
(...):gsub(
    -- Match key-value pairs
    '(%w-)=(%w-)',
    -- For each pair, run callback (braces are replaced for multiline)
    load[[
        k,v=... -- Assign key, value
        o=t[k] -- Look for already stored string if any
        l[#l+1]=not o and k or _ -- If key is new, store it in list
        t[k]=o and o..','..v or v -- Append to string if it is not new, store it if it is
    ]]
)
-- For every record in list
for i=1,#l do
    -- Write with no newlines
    io.write(
        i>1 and'&'or'', -- Output & before all values but first
        l[i],'=',t[l[i]] -- Print key-value pair
    )
end
2 LegionMammal978 Oct 01 2020 at 13:02

Wolfram Language (Mathematica) , 86 octets

StringRiffle[Last@Reap[Sow@@@StringExtract[#,"&"->;;,"="->{2,1}],_,List],"&","=",","]&

Essayez-le en ligne! Fonction pure, prend une chaîne en entrée et renvoie une autre chaîne en sortie. Ceci est inspiré et très similaire à la réponse d' att , mais il utilise un algorithme similaire à celui du billet de blog (ici utilisant Sow/ Reap). Voici un exemple de l'évaluation des sous-expressions sur une entrée de "foo=1&bar=a&foo=":

StringExtract[#,"&"->;;,"="->{2,1}] == {{"1", "foo"}, {"a", "bar"}, {"", "foo"}}
Sow@@@...                           == {"1", "a", ""}; {"1", ""} sown w/ tag "foo"; {"a"} sown w/ tag "bar"
Reap[...,_,List]                    == {{"1", "a", ""}, {{"foo", {"1", ""}}, {"bar", {"a"}}}}
Last@...                            == {{"foo", {"1", ""}}, {"bar", {"a"}}}
StringRiffle[...,"&","=",","]       == "foo=1,&bar=a"
2 GalenIvanov Oct 01 2020 at 18:37

Facteur , 229 octets

: c ( s -- s s ) 1 <hashtable> swap "&"split [ "="split ] map
[ [ dup [ last ] dip first pick push-at ] each ]
[ [ first ] map dup union ] bi dup [ [ over at ","join ] map ] dip
[ "="append ] map swap zip [ concat ] map "&"join ;

Essayez-le en ligne!

C'est long mais j'en suis un peu content :)

2 Giuseppe Sep 30 2020 at 16:00

SNOBOL4 (CSNOBOL4) , 243 221 212 octets

	Q =INPUT
	T =TABLE()
N	Q (ARB '=') . L ARB . V ('&' | RPOS(0)) REM . Q	:F(O)
	T<L> =T<L> ',' V 	:(N)
O	R =CONVERT(T,'ARRAY')
I	X =X + 1
	R<X,2> ',' REM . V 	:F(P)
	O =O '&' R<X,1> V	:(I)
P	O '&' REM . OUTPUT
END

Essayez-le en ligne!

TABLEdans SNOBOL est bizarre. C'est parfaitement bien d'accepter un PATTERNlike ARBcomme clé, mais pas la chaîne vide ''. Cependant, utiliser <label>=comme étiquette au lieu de <label>résout parfaitement ce problème.

Explication d'une itération précédente:

	E =RPOS(0)					;* alias for end of string 
	A =ARB						;* alias for ARBitrary match (as short as possible)
	Q =INPUT					;* read input
	T =TABLE()					;* create a TABLE (a dictionary)
N	Q A . L '=' A . V ('&' | E) REM . Q	:F(O)	;* in regex land, this is something like
	;* '(.*=)(.*)(&|$)' where you save \1 and \2 as L and V, respectively. If there's no match, goto O
	T<L> =T<L> V ','	:(N)		;* update the values list, then goto N
O	R =CONVERT(T,'ARRAY')				;* convert T to a 2D array of [Label,Value]
I	X =X + 1					;* increment array index
	R<X,2> A . V ',' E	:F(P)			;* remove the trailing ',' from the value list. If X is out of bounds, goto P
	O =O R<X,1> V '&'	:(I)			;* Append L and V to O with an '=' and '&', then goto I
P	O A . OUTPUT '&' E				;* Print everything except for the trailing '&'
END
2 Neil Oct 09 2020 at 12:50

PHP , 146 octets

<?=parse_str(str_replace('=','_[]=',$argv[1]),$a)??join('&',array_map(function($b,$c){return rtrim($b,'_').'='.join(',',$c);},array_keys($a),$a));

Essayez-le en ligne! Explication: parse_strn'a pas été conçu pour gérer des valeurs répétées, mais vous pouvez le persuader en nommant chaque valeur avec une fin []. Il n'a pas non plus été conçu pour gérer les noms vides, mais puisque []j'ajoute de toute façon, je peux également ajouter un _pour satisfaire ce cas. Après avoir analysé la chaîne de requête, il reste ensuite à tout rassembler.

1 GalenIvanov Sep 30 2020 at 11:34

Rouge , 221 octets

func[s][m: copy #()s: split s"&"forall s[s/1: split s/1"="append s/1/1"="put m s/1/1
copy""]foreach v s[m/(v/1): append m/(v/1) rejoin[v/2","]]t: copy""foreach k keys-of
m[take/last m/:k repend t[k m/:k"&"]]take/last t t]

Essayez-le en ligne!

C'est terriblement long et je vais essayer de jouer au golf au moins un peu. Dommage que le rouge n'ait pas de joinfonction pratique ...

1 Neil Sep 30 2020 at 19:18

Charbon , 42 octets

≔E⪪S&⪪ι=θW⁻Eθ§κ⁰υ⊞υ§ι⁰⪫Eυ⁺⁺ι=⪫EΦθ¬⌕λι⊟λ,¦&

Essayez-le en ligne! Le lien est vers la version verbeuse du code. Explication:

≔E⪪S&⪪ι=θ

Divisez l'entrée sur &s et divisez chaque jeton sur =s.

W⁻Eθ§κ⁰υ⊞υ§ι⁰

Créez une liste de clés uniques dans l'ordre de leur première apparition.

⪫Eυ⁺⁺ι=⪫EΦθ¬⌕λι⊟λ,¦&

Pour chaque clé, extrayez et joignez les valeurs avec ,, concaténez avec la clé et le séparateur, puis joignez le résultat global avec &.

1 ErikF Sep 30 2020 at 05:36

C (gcc) , 319304 octets

Merci à plafonnier pour les suggestions.

Cette fonction scanne chaque paire tokenisée (nom, valeur) et ajoute une entrée de liste pour chaque nouveau nom rencontré, puis ajoute une entrée de liste pour chaque valeur. Après avoir construit les listes, il parcourt ensuite chaque liste et imprime les valeurs. Pour économiser de l'espace, j'ai aplati les structures en tableaux de void *.

f(s,t,v,i)char*s,*t;{void*d[3]={t=0},**f,**w;for(;t=strtok(t?0:s,"&");*w=calloc(8,2),w[1]=v){t[i=strcspn(t,"=")]=0;v=t-~i;for(f=&d;strcmp(f[1]?:t,t);f=*f);for(w=f[2]=f[1]?f[2]:(f[1]=t,*f=calloc(8,5))+24;w[1];w=*w);}for(f=&d;i=*f&&printf("&%s="+!!s,f[1]);f=*f)for(w=f[2];s=*w;w=s)i=!printf(",%s"+i,w[1]);}

Essayez-le en ligne!

Version non golfée de la soumission originale:

struct list {
  struct list *next;
  char *name;
  struct list *value;
};

void f(char *s) {
  char *tok=NULL, *value;
  struct list d={}, *e, *v;
  int i;

  for(; tok=strtok(tok?NULL:s, "&"); ) {
    tok[i=strcspn(tok, "=")]=0;
    value=tok+i+1;
    for(e=&d; e->name && strcmp(e->name, tok); e=e->next);
    if(!e->name) {
      e->next=calloc(sizeof(struct list), 2);
      e->name=tok;
      e->value=e->next+1;
    }
    for(v=e->value; v->name; v=v->next);
    v->next=calloc(sizeof(struct list), 1);
    v->name=value;
  }
  for(e=&d; e->next; e=e->next, s=0) {
    printf("&%s="+!!s, e->name);
    for(v=e->value, i=1; v->next; v=v->next, i=0)
      printf(",%s"+i, v->name);
  }
}

Essayez-le en ligne!

1 att Sep 30 2020 at 07:25

Wolfram Language (Mathematica) , 90 88 octets

StringRiffle[S=StringSplit;List@@@Normal@Merge[Rule@@@S[#~S~"&","=",2],#&],"&","=",","]&

Essayez-le en ligne!

-2 merci à LegionMammal978

S=StringSplit;
Rule@@@S[#~S~"&","=",2]         (* Convert to a list of Rules *)
Merge[ % ,#&]                   (* Combine rules into an Association, leaving values unchanged *)
Normal@ %                       (* Convert Association back into a list of Rules, *)
List@@@ %                       (* and turn Rules into Lists *)
StringRiffle[ % ,"&","=",","]   (* Concatenate, using "&", "=", and "," as separators *)
1 Chris Sep 29 2020 at 22:03

Perl 5 , -pF\&indicateurs, 73 57 octets

Utilise -pF\&pour boucler sur les entrées et répartir automatiquement &.

Résultats désordonnés, en concurrence parallèle.

/=/,$z{$`}.=$z{$`}?",$'":"$`=$'"for@F;$_=join'&',values%z

Essayez-le en ligne!

Utilise un hachage %zpour garder une trace des valeurs des noms individuels, puis les imprime tous à la fin. -16 octets grâce à NahuelFouilleul.

1 DominicvanEssen Sep 30 2020 at 09:26

R , 174 166 octets

function(s,S=strsplit,A=sapply,P=paste,e=A(S(P(el(S(s,'&')),'=',sep=''),'='),c),n=unique(m<-e[1,]))P(n,A(n,function(x)P(e[2,m==x],collapse=',')),sep='=',collapse='&')

Essayez-le en ligne!

Pour une raison inexplicable, j'ai pensé que ce défi ne souffrirait pas de la gestion des chaînes terriblement verbeuse de R.
Cela ne s'est pas avéré être le cas, du moins sur la base de ma tentative jusqu'à présent ...

Commenté avant de jouer au golf:

compactify=
function(s,                         # s = string to compactify
S=strsplit,                         # S = alias to strsplit() function
A=sapply,                           # A = alias to sapply() function
P=paste,                            # P = alias to paste() function
a=el(                               # a = the first element of ...
  S(s,'&'))                         #  ...s split on '&'
b=S(a,,'=')                         # b = each element of a, split on '='
                                    # Now, unfortunately if there's nothing after the '=',
                                    # the strsplit() function fails to add an empty string ''
                                    # so we need to do this ourselves:
e=A(b,function(x)c(x,'')            # e = for each element of b, add a '' ...  
                        [1:2])      #  ...and then take the first two elements
                                    # This gives us a 2-row matrix, with the names in row 1,
                                    # and the values in row 2
n=unique(m<-e[1,]))                 # n = unique names, m = all names of name-value pairs
m=A(n,function(x)                   # m = for each element of n...
      P(e[2,m==x],collapse=','))    #  ...paste together the values for this name, using ',' as separator
P(n,m,sep='=',collapse='&')         # Finally, paste together the pairs of elements in m, using '=' as separator...
                                    #  ...and collapse them all into one string using '&' as separator
1 Giuseppe Sep 30 2020 at 19:07

R , 162 157 151 octets

function(s,S=substring)paste0(z<-unique(k<-S(x<-el(strsplit(s,"&")),1,y<-regexpr('=',x))),sapply(split(S(x,y+1),k),paste,collapse=',')[z],collapse='&')

Essayez-le en ligne!

-6 octets grâce à Dominic van Essen

Une belle fonction sur une seule ligne. Non golfé:

function(s,S=substr){
pairs <- el(strsplit(s,"&"))					# split on '&' getting list of pairs
loc <- regexpr('=',pairs)					# find location of each '=' in each pair 
keys <- substr(pairs,1,loc)					# get the key, including the '='
values <- substr(pairs,loc + 1,nchar(pairs))			# get the values (everything after '=')
unq_keys <- unique(keys)					# uniquify the keys. This retains the order.
split_vals <- split(values,keys)				# group the values into sublists by which key they are associated with
collapsed_values <- sapply(split_vals,paste,collapse=',')	# join each subgroup of values by ','
collapsed_values <- collapsed_values[unq_keys]			# and reorder them to match the order of the keys
paste0(unq_keys,collapsed_values,collapse='&')			# concatenate keys and values and join by '&'
}