Compactar la cadena de consulta
Introducción
Solo en las noticias de Hacker, John Resig contempla transformar una consulta "foo = 1 & foo = 2 & foo = 3 & blah = a & blah = b" en una que se vea así: "foo = 1,2,3 & blah = a, b", https://johnresig.com/blog/search-and-dont-replace/. Afirma "ser 10 líneas más corto que la solución de Mark".
Las cadenas de consulta constan de secuencias de pares nombre-valor. Los pares de nombre-valor constan de un nombre y un valor, separados por =. los nombres y valores son posiblemente secuencias vacías de caracteres alfanuméricos. Los pares de nombre-valor están separados por el carácter & en la secuencia. Los valores son únicos para cada nombre.
Desafío
10 líneas más cortas que la solución de Mark no son suficientes.
- Leer una cadena de consulta.
- Combine pares de nombre y valor con el mismo nombre en un solo par de nombre y valor, con valores concatenados y separados por comas.
- Genere la cadena de consulta combinada, con pares de nombre-valor en el orden en que se encuentran los nombres de izquierda a derecha en la cadena de consulta.
Esto es código de golf, las lagunas estándar están cerradas.
Ejemplo de entrada y salida
Entrada:
foo = 1 & foo = & blah = a & foo = 3 & bar = x & blah = b & = 1 & = 2
Salida:
foo = 1`` 3 & blah = a, b & bar = x & = 1,2
Entrada:
foo = bar & foo = foo
Salida:
foo = bar, foo
Respuestas
05AB1E , 23 bytes
'&¡'=δ¡.¡н}εø€Ù}…,=&vyý
Pruébelo en línea o verifique todos los casos de prueba .
Explicación:
'&¡ '# 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)
Pruébelo en línea con un resultado paso a paso.
JavaScript (ES6), 103 99 87 86 bytes
Guardado 1 byte gracias a @MatthewJensen
s=>Object.values(o=/(\w*=)(\w*)/g,s.replace(o,(s,k,v)=>o[k]=o[k]?o[k]+[,v]:s)).join`&`
¡Pruébelo en línea!
Comentado
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 '&'
JavaScript, 263 201 196 194 190 189 188 173 161 bytes
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}
Pruébelo en línea
Julia 1.0 , 106 bytes
f(a,z=split.(split(a,'&'),'='),u=first.(z))=join((i*'='*join(last.(z)[u.==i],',') for i in unique(u)),"&")
¡Pruébelo en línea!
Python 3.8 (versión preliminar) , 116 bytes
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))
¡Pruébelo en línea!
-46 bytes gracias a ovs
-1 byte gracias a Jonathan Allan (en Py 3.8 PR, con morsa)
Ruby , 88 80 76 64 bytes
->s{s.scan(/(\w*=)(\w*)/).group_by(&:shift).map{|k,v|k+v*?,}*?&}
¡Pruébelo en línea!
-8 bytes gracias a ovs por apuntar pude asignar una lambda a una variable
-12 bytes gracias a Dingus!
Python 3 , 94 bytes
lambda s:'&'.join(k+'='+",".join(v)for k,v in parse_qs(s,1).items())
from urllib.parse import*
¡Pruébelo en línea!
Unas pocas cosas:
- Esto solo funciona porque Python 3.6+ mantiene el orden del diccionario según el momento de la inserción. Nos encantaría usar Python 2 para una importación más corta (
urlparsevsurllib.parse), pero los diccionarios no están ordenados correctamente. - Por mucho que me gusten las cuerdas f,
k+'='+",".join(v)es más corto quef'{k}={",".join(v)}'. - El doble se
.joinsiente como olor a golf, pero no encuentro un camino más corto.
Python 3 , 95 bytes
from urllib.parse import*
d=parse_qs(input(),1)
print('&'.join(k+'='+','.join(d[k])for k in d))
¡Pruébelo en línea!
Gelatina , 28 bytes
-2 ¡Gracias a Zgarb!
ṣ”&ṣ€”=Ṗ€ĠṢịƲµZQ€j€”,j”=)j”&
Un Link monádico que acepta y entrega una lista de personajes.
¡Pruébelo en línea!
¿Cómo?
ṣ”&ṣ€”=Ṗ€ĠṢịƲµ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
05AB1E , 62 51 bytes
(-11 de @kevin)
'&¡'=δ¡D€нÙÐV_UsvYyнk©Xsèyθ',««Xs®ǝU}X妨}Yζí'=ý'&ý
¡Pruébelo en línea!
Mi enfoque 62:
'&¡ε'=¡}D€нÙ©DgÅ0Usvy¬®skDVXsèyθ',««XsYǝU}X妨}®ζεć'=s««˜}˜'&ý
Explicación:
'&¡ε'=¡}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 &
¡Pruébelo en línea!
JavaScript, 79 bytes
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 las E / S podrían ser cadenas de consulta reales de acuerdo con la especificación WHATWG, en lugar de cadenas no válidas que parecen cadenas de consulta pero que no están codificadas en URL correctamente, entonces se podrían guardar 7 bytes especificando el resultado en cadena en lugar de eliminar el escape.
JavaScript, 106 104 76 bytes
La solución Retina del puerto de Neil , publicada con permiso.
f=q=>q==(q=q.replace(/(?<=^|&)((\w*=)[^&]*)(.*?)&\2(\w*)/,`$1,$4$3`))?q:f(q)
¡Pruébelo en línea!
Original
Un poco de golf borracho en el que volví a trabajar sobrio, pero en el proceso descubrí la solución de Arnauld y me di cuenta de que estaba en el camino hacia algo casi idéntico, así que lo dejé como estaba.
q=>Object.keys(o={},q.split`&`.map(p=>o[[k,v]=p.split`=`,k]=[...o[k]||[],v])).map(x=>x+`=`+o[x]).join`&`
¡Pruébelo en línea!
Retina 0.8.2 , 45 bytes
+1`(?<=^|&)((\w*=)[^&]*)(.*?)&\2(\w*)
$1,$4$3
¡Pruébelo en línea! Explicación: Hace coincidir repetidamente la primera clave duplicada y su primer duplicado y une el valor al de la clave original.
Japón , 29 28 27 26 24 bytes
q& móÈk¶
ü@bøXÎîÕvÎqÃq&
Intentalo
Guardado 2 bytes gracias a la inspiración de Kevin .
(Oh, si Japt solo tuviera un método solo para agrupar, en lugar de ordenar también, esto podría ser de 19 bytes ).
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 "&"
O, para proporcionar un tutorial paso a paso:
Entrada
"foo=1&foo=&blah=a&foo=3&bar=x&blah=b&=1&=2"
División
["foo=1","foo=","blah=a","foo=3","bar=x","blah=b","=1","=2"]
Mapa y partición
[["foo=","1"],["foo="],["blah=","a"],["foo=","3"],["bar=","x"],["blah=","b"],["=","1"],["=","2"]]
Agrupar y ordenar
[[["foo=","1"],["foo="],["foo=","3"]],[["blah=","a"],["blah=","b"]],[["bar=","x"]],[["=","1"],["=","2"]]]
Mapa y ...
Transponer
[[["foo=","foo=","foo="],["1",null,"3"]],[["blah=","blah="],["a","b"]],[["bar="],["x"]],[["=","="],["1","2"]]]
Asignar el primer elemento a su primer elemento
[["foo=",["1",null,"3"]],["blah=",["a","b"]],["bar=",["x"]],["=",["1","2"]]]
Unirse
["foo=1,,3","blah=a,b","bar=x","=1,2"]
Unirse
"foo=1,,3&blah=a,b&bar=x&=1,2"
Awk , 144 \$\cdots\$144138 bytes
Se agregaron 6 bytes para corregir un error.
Se agregaron 4 bytes para corregir un error amablemente señalado y resuelto por Dominic van Essen .
Se ahorraron 6 bytes gracias a 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="&"}}
¡Pruébelo en línea!
R , 195 bytes
{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)-'&'})}
¡Pruébelo en línea!
Lua , 162 bytes
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
¡Pruébelo en línea!
Este es largo, eh. Podría haberse hecho mucho más corto si no fuera por pedir un requisito.
Explicación:
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
Wolfram Language (Mathematica) , 86 bytes
StringRiffle[Last@Reap[Sow@@@StringExtract[#,"&"->;;,"="->{2,1}],_,List],"&","=",","]&
¡Pruébelo en línea! Función pura, toma una cadena como entrada y devuelve otra cadena como salida. Esto está inspirado y es muy similar a la respuesta de att , pero utiliza un algoritmo similar al de la publicación del blog (aquí utilizando Sow/ Reap). A continuación, se muestra un ejemplo de cómo se evalúan las subexpresiones en una entrada 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"
Factor , 229 bytes
: 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 ;
¡Pruébelo en línea!
Es largo pero estoy algo contento con él :)
SNOBOL4 (CSNOBOL4) , 243 221 212 bytes
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
¡Pruébelo en línea!
TABLEen SNOBOL es raro. Está perfectamente bien aceptar un me PATTERNgusta ARBcomo clave, pero no la cadena vacía ''. Sin embargo, usarlo <label>=como etiqueta en lugar de <label>resuelve perfectamente este problema.
Explicación de una iteración anterior:
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
PHP , 146 bytes
<?=parse_str(str_replace('=','_[]=',$argv[1]),$a)??join('&',array_map(function($b,$c){return rtrim($b,'_').'='.join(',',$c);},array_keys($a),$a));
¡Pruébelo en línea! Explicación: parse_strno fue diseñado para manejar valores repetidos, pero puedes persuadirlo nombrando cada valor con un final []. Tampoco fue diseñado para manejar nombres vacíos, pero como estoy agregando de []todos modos, también puedo agregar un _para satisfacer ese caso. Una vez analizada la cadena de consulta, queda por volver a unir todo.
Rojo , 221 bytes
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]
¡Pruébelo en línea!
Esto es muy largo y trataré de jugar al golf al menos un poco. Lástima que el rojo no tenga una joinfunción útil ...
Carbón , 42 bytes
≔E⪪S&⪪ι=θW⁻Eθ§κ⁰υ⊞υ§ι⁰⪫Eυ⁺⁺ι=⪫EΦθ¬⌕λι⊟λ,¦&
¡Pruébelo en línea! El enlace corresponde a la versión detallada del código. Explicación:
≔E⪪S&⪪ι=θ
Divida la entrada en &sy divida cada token en =s.
W⁻Eθ§κ⁰υ⊞υ§ι⁰
Cree una lista de claves únicas en el orden de su primera aparición.
⪫Eυ⁺⁺ι=⪫EΦθ¬⌕λι⊟λ,¦&
Para cada clave, extrae y une los valores con ,, concatenar con la clave y el separador, y unir el resultado general con &.
C (gcc) , 319304 bytes
Gracias a roofcat por las sugerencias.
Esta función escanea cada par tokenizado (nombre, valor) y agrega una entrada de lista para cada nuevo nombre encontrado, luego agrega una entrada de lista para cada valor. Después de construir las listas, recorre cada lista e imprime los valores. Para ahorrar espacio, aplané las estructuras en matrices 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]);}
¡Pruébelo en línea!
Versión sin golf de la presentación original:
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);
}
}
¡Pruébelo en línea!
Wolfram Language (Mathematica) , 90 88 bytes
StringRiffle[S=StringSplit;List@@@Normal@Merge[Rule@@@S[#~S~"&","=",2],#&],"&","=",","]&
¡Pruébelo en línea!
-2 gracias a 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 *)
Perl 5 , -pF\&banderas, 73 57 bytes
Se utiliza -pF\¶ realizar un bucle en las entradas y realizar la división automática &.
Resultados desordenados, en competición paralela.
/=/,$z{$`}.=$z{$`}?",$'":"$`=$'"for@F;$_=join'&',values%z
¡Pruébelo en línea!
Utiliza un hash %zpara realizar un seguimiento de los valores de los nombres individuales y luego los imprime todos al final. -16 bytes gracias a NahuelFouilleul.
R , 174166 bytes
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='&')
¡Pruébelo en línea!
Por alguna razón inexplicable, pensé que este desafío no se vería afectado por el manejo de cadenas terriblemente detallado de R.
Este no resultó ser el caso, al menos según mi intento hasta ahora ...
Comentado antes de jugar al 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
R , 162 157 151 bytes
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='&')
¡Pruébelo en línea!
-6 bytes gracias a Dominic van Essen
Una bonita función de una sola línea. Sin 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 '&'
}