Consejos para jugar golf en ECMAScript 6 y superior


88

Esto es similar a otros "Consejos para jugar golf en <...>", pero apunta específicamente a las funciones más nuevas en JavaScript presentadas en ECMAScript 6 y superiores.

JavaScript es un lenguaje inherentemente muy prolijo, function(){}, .forEach(), la conversión de cadena a la matriz, matriz de objeto similar a la matriz, etc, etc, son súper hincha y no es saludable para jugar al golf.

ES6 +, por otro lado, tiene algunas características súper prácticas y una huella reducida. x=>y, [...x]etc. son solo algunos de los ejemplos.

Por favor, publique algunos trucos agradables que pueden ayudar a eliminar esos pocos bytes adicionales de su código.

NOTA: Los trucos para ES5 ya están disponibles en Consejos para jugar golf en JavaScript ; Las respuestas a este hilo deben centrarse en trucos solo disponibles en ES6 y otras versiones futuras de ES.

Sin embargo, este hilo también es para usuarios que actualmente juegan golf usando las funciones de ES5. Las respuestas también pueden contener consejos para ayudarlos a comprender y asignar las funciones de ES6 a su estilo de codificación ES5.

Respuestas:


42

Operador extendido ...

El operador de propagación transforma un valor de matriz en una lista separada por comas.

Caso de uso 1:

Use directamente una matriz donde una función espera una lista

list=[1,2,3]
x=Math.min(...list)
list=[10,20], a.push(...list) // similar to concat()

Caso de uso 2:

Crear una matriz literal a partir de un iterable (generalmente una cadena)

[...'buzzfizz'] // -> same as .split('')

Caso de uso 3:

Declarar un número variable de argumentos para una función.

F=(...x) => x.map(v => v+1)
// example: F(1,2,3) == [2,3,4]

Ver documento de Mozilla


3
Ahora tengo un voto negativo aquí. Obviamente, alguien observó algo terriblemente mal en este consejo, por ser demasiado tímido para dejar un comentario y explicar lo que ...
edc65

Se ve bien Tal vez fue la falta de punto y coma? ;) (por cierto, también puede usarlo como parámetros de descanso, como símbolos en Ruby)
gcampbell

Podría agregar que también tiene un caso de uso en firmas de funciones :)
Felix Dombek

Cliquear mal no significaba votar en contra
Stan Strum el

@StanStrum sucede. Haré una pequeña actualización de esta publicación para que eventualmente pueda cambiar su voto (¿o ya lo hizo?)
edc65

21

Trucos aprendidos aquí desde que me uní

Mi lenguaje de programación principal es JS y principalmente ES6. Desde que me uní a este sitio hace una semana, he aprendido muchos trucos útiles de otros miembros. Estoy combinando algunos de esos aquí. Todos los créditos a la comunidad.

Funciones de flecha y bucles

Todos sabemos que las funciones de flecha guardan muchos byts

function A(){do something} // from this
A=a=>do something // to this

Pero debes tener en cuenta algunas cosas

  • Intente agrupar varias declaraciones utilizando, por ,ejemplo, (a=b,a.map(d))- Aquí, el valor que se devuelve es la última expresióna.map(d)
  • Si su do somethingparte es más de una declaración, debe agregar los {}corchetes circundantes .
  • Si hay {}corchetes circundantes , debe agregar una declaración de retorno explícita.

Lo anterior es cierto muchas veces cuando tienes bucles involucrados. Entonces algo como:

u=n=>{for(s=[,1,1],r=[i=1,l=2];c=l<n;i+=!c?s[r[l++]=i]=1:1)for(j of r)c-=j<i/2&s[i-j];return n>1?r:[1]}

Aquí estoy desperdiciando al menos 9 caracteres debido a la devolución. Esto puede ser optimizado.

  • Intenta evitar bucles. Use .mapo .everyo en su .somelugar. Tenga en cuenta que si desea cambiar la misma matriz que está mapeando, fallará.
  • Envuelva el bucle en una función de flecha de cierre, convirtiendo la función de flecha principal como una sola declaración.

Entonces lo anterior se convierte en:

u=n=>(s=>{for(r=[i=1,l=2];c=l<n;i+=!c?s[r[l++]=i]=1:1)for(j of r)c-=j<i/2&s[i-j]})([,1,1])|n>1?r:[1]

caracteres eliminados: {}return

caracteres añadidos: (){}>|

Tenga en cuenta cómo llamo al método de cierre, que llena correctamente la variable ny luego, dado que el método de cierre no devuelve nada (es decir, regresa undefined), lo hago a bit o lo devuelvo ntodo en una sola declaración de la función de flecha exterioru

Comas y punto y coma

Evítalos lo que sea,

Si está declarando variables en un bucle, o como se mencionó en la sección anterior, usando ,declaraciones separadas para tener funciones de flecha de declaración única, entonces puede usar algunos trucos bastante ingeniosos para evitarlos ,o ;eliminar esos últimos bytes.

Considera este código:

r=v=>Math.random()*100|0;n=r();m=r();D=v=>A(n-x)+A(m-y);d=0;do{g();l=d;d=D();....

Aquí, estoy llamando a muchos métodos para inicializar muchas variables. Cada inicialización está usando un ,o ;. Esto puede reescribirse como:

r=v=>Math.random()*100|0;n=r(m=r(d=0));D=v=>A(n-x)+A(m-y);do{d=D(l=d,g());....

Observe cómo uso el hecho de que el método no molesta a la variable que se le pasa y lo uso para reducir 3 bytes.

Misceláneos

.search en lugar de .indexOf

Ambos dan el mismo resultado, pero searches más corto. Aunque la búsqueda espera una expresión regular, úsela sabiamente.

`Cadenas de plantilla`

Estos son muy útiles cuando tiene que concatenar una o más partes de la cadena en función de ciertas condiciones.

Tome el siguiente ejemplo para generar una quine en JS

(f=x=>alert("(f="+f+")()"))()

vs.

(f=x=>alert(`(f=${f})()`))()

En una cadena de plantilla, que es una cadena dentro de dos comillas inversas (`), cualquier cosa dentro de a ${ }se trata como un código y se evalúa para insertar la respuesta resultante en la cadena.

Publicaré algunos trucos más tarde. ¡Feliz golf!


1
.search es más corto, ¡úselo cuando sea posible! pero no es lo mismo de .indexOf. .search quiere una regexp, no una cadena. Prueba'abc'.search('.')
edc65

@ edc65 ¡Actualizado!
Optimizador

Puede modificar la matriz original con los métodos de instancia. El segundo es el índice actual, y el tercero es la matriz que se está iterando.
Isiah Meadows

8
"Se unió al sitio hace una semana" - 21.4k rep ...
GamrCorps

2
Además .map, la recursión es otra técnica que a veces puede ayudarlo a convertir un forbucle en una expresión.
Neil

20

Usar shorthands de propiedad

Las abreviaturas de propiedad le permiten establecer variables a los valores de una matriz:

a=r[0];b=r[1] // ES5
[a,b]=r       // ES6 - 6 bytes saved

Esto también se puede usar como:

a=r[0],b=r[2] // ES5
[a,,b]=r      // ES6 - 5 bytes saved

Incluso puede usar esto para revertir variables:

c=a,a=b,b=c // ES5 - uses extra variable
[b,a]=[a,b] // ES6 - not shorter, but more flexible

También puede usar esto para acortar slice()funciones.

z = [1, 2, 3, 4, 5];

a=z.slice(1) // a = [2,3,4,5]; ES5
[,...a]=z    // a = [2,3,4,5]; ES6

Conversiones base

ES6 proporciona una forma mucho más corta de convertir los formularios Base-2 (binario) y Base-8 (octal) a decimal:

0b111110111 // == 503
0o767       // == 503

+se puede usar para convertir una cadena binaria, octal o hexadecimal a un número decimal. Se puede utilizar 0b, 0oy 0x, si es binario, octal y hexadecimal, respectivamente .:

parseInt(v,2) // ES5
+('0b'+v)     // ES6 - 4 bytes saved; use '0o' for octal and '0x' for hex
'0b'+v-0      // Shorter, but may not work in all cases
              // You can adapt this your case for better results

Si está usando esto> 7 veces, será más corto de usar parseInty cambiarle el nombre:

(p=parseInt)(v,2)

Ahora pse puede usar para parseIntahorrar muchos bytes a largo plazo.


El truco de conversión de base es bueno, pero es más probable que el número de conversión sea en forma de una variable en lugar de un literal, en cuyo caso, se vuelve mucho más largo.
Optimizador

1
'0x'+v-0es incluso más corto, pero puede no funcionar tan bien en algunos escenarios.
ETHproductions

1
Por cierto, 0767(ES5) es más corto que la 0o767notación (ES6).
Camilo Martin

@CamiloMartin 0767es una extensión no estándar y está explícitamente prohibida en modo estricto.
Oriol

1
El modo estricto @Oriol fue un mal meme. No ayudó al rendimiento, realmente no lo obligó a escribir un buen código y nunca se convertiría en el valor predeterminado de todos modos. 0los literales octales prefijados no van a ninguna parte, y son tan válidos como ecmascript 0o.
Camilo Martin

19

Usar plantillas de cadena con funciones

Cuando tiene una función con una cadena como argumentos. Puede omitir el ()si no tiene ninguna expresión:

join`` // Works
join`foobar` // Works
join`${5}` // Doesn't work 

99
Ten en cuenta que esto realmente pasa una matriz. fun`string` es lo mismo que fun(["string"])no fun("string"). Esto está bien para funciones que se convierten en cadenas, como alert, pero para otros esto podría causar problemas. Para obtener más información, consulte el artículo de MDN
Cyoce,

55
Referencia rápida: fun`foo${1}bar${2}bazes equivalente a llamarfun(["foo","bar","baz"],1,2)
Cyoce

14

Comprensiones de matriz (Firefox 30-57)

Nota: las comprensiones de matriz nunca se estandarizaron y quedaron obsoletas con Firefox 58. Úselo bajo su propio riesgo.


Originalmente, la especificación ECMAScript 7 contenía un montón de nuevas características basadas en matrices. Aunque la mayoría de estos no llegaron a la versión finalizada, Firefox admite (ed) posiblemente la mayor de estas características: una nueva sintaxis elegante que puede reemplazar .filtery .mapcon for(a of b)sintaxis. Aquí hay un ejemplo:

b.filter(a=>/\s/.test(a)).map(a=>a.length)
[for(a of b)if(/\s/.test(a))a.length]

Como puede ver, las dos líneas no son tan diferentes, aparte de la segunda que no contiene las palabras clave voluminosas y las funciones de flecha. Pero esto solo explica el pedido .filter().map(); ¿Qué pasa si tienes en su .map().filter()lugar? Realmente depende de la situación:

b.map(a=>a[0]).filter(a=>a<'['&&a>'@')
[for(a of b)if(a<'['&&a>'@')a[0]]

b.map(a=>c.indexOf(a)).filter(a=>a>-1)
[for(a of b)if((d=c.indexOf(a))>-1)d]

b.map(a=>a.toString(2)).filter(a=>/01/.test(a))
[for(a of b)if(/01/.test(c=a.toString(2)))c]

O lo que si quieres , ya sea .map o .filter ? Bueno, por lo general resulta menos aceptable:

b.map(a=>a.toString(2))
[for(a of b)a.toString(2)]

b.filter(a=>a%3&&a%5)
[for(a of b)if(a%3&&a%5)a]

Por lo tanto, mi consejo es usar las comprensiones de matriz donde sea que usas habitualmente .map y .filter , pero no solo una u otra.

Comprensiones de cuerdas

Lo bueno de las comprensiones de ES7 es que, a diferencia de las funciones específicas de la matriz como .mapy .filter, se pueden usar en cualquier objeto iterable, no solo en las matrices. Esto es especialmente útil cuando se trata de cadenas. Por ejemplo, si desea ejecutar cada carácter cen una cadena a través de c.charCodeAt():

x=>[...x].map(c=>c.charCodeAt())
x=>[for(c of x)c.charCodeAt()]

Eso es dos bytes guardados en una escala bastante pequeña. ¿Y qué pasa si quieres filtrar ciertos caracteres en una cadena? Por ejemplo, este solo guarda letras mayúsculas:

x=>[...x].filter(c=>c<'['&&c>'@')
x=>[for(c of x)if(c<'['&&c>'@')c]

Hmm, eso no es más corto. Pero si combinamos los dos:

x=>[...x].filter(c=>c<'['&&c>'@').map(c=>c.charCodeAt())
x=>[for(c of x)if(c<'['&&c>'@')c.charCodeAt()]

¡Guau, un total de 10 bytes guardados!

Otra ventaja de la comprensión de cadenas es que las cadenas codificadas ahorran un byte adicional, ya que puede omitir el espacio después de of:

x=>[...'[](){}<>'].map(c=>x.split(c).length-1)
x=>[for(c of'[](){}<>')x.split(c).length-1]

x=>[...'[](){}<>'].filter(c=>x.split(c).length>3)
x=>[for(c of'[](){}<>')if(x.split(c).length>3)c]

Indexación

Las comprensiones de matriz hacen que sea un poco más difícil obtener el índice actual en la cadena / matriz, pero se puede hacer:

a.map((x,i)=>x+i).filter ((x,i)=>~i%2)
[for(x of(i=0,a))if(++i%2)x+i-1]

Lo principal a tener en cuenta es asegurarse de que el índice se incremente cada vez, no solo cuando se cumple una condición.

Generador de comprensiones

Las comprensiones de generador tienen básicamente la misma sintaxis que las comprensiones de matriz; simplemente reemplace los corchetes con paréntesis:

x=>(for(c of x)if(c<'['&&c>'@')c.charCodeAt())

Esto crea un generador que funciona de la misma manera que una matriz, pero esa es una historia para otra respuesta.

Resumen

Básicamente, aunque las comprensiones son generalmente más cortas que .map().filter(), todo se reduce a los detalles de la situación. Es mejor intentarlo en ambos sentidos y ver cuál funciona mejor.

PD ¡No dudes en sugerir otro consejo relacionado con la comprensión o una forma de mejorar esta respuesta!


He aquí un truco para los rangos que va a guardar un par de caracteres:(x,y)=>[...Array(y-x)].map(a=>x++)
Mwr247

2
Puede cortar otros 11 bytes para hacer un rango de 0 a x:x=>[...Array(x).keys()]
Mwr247

El último para la comprensión allí: n=>[for(x of Array(n).keys())if(/1/.test(x))x](ahorra 7 bytes)
Mwr247

@ Mwr247 En realidad, ahora puedo ver que los rangos generalmente no son tan cortos con las comprensiones como con otras buenas características de ES6. Agregaré una sección sobre cadenas en su lugar, y te dejaré manejar los rangos.
ETHproductions

Vale la pena señalar que las comprensiones de matriz se han desaprobado y eliminado de todas las versiones recientes de javascript. Vea los documentos de MDN sobre el tema.
Keefer Rourke

13

Las expresiones de función en ES6 usan la notación de flecha, y ayuda mucho a ahorrar bytes si se compara con la versión ES5:

f=function(x,y){return x+y}
f=(x,y)=>x+y

Si su función solo tiene un parámetro, puede omitir los paréntesis para guardar dos bytes:

f=x=>x+1

Si su función no tiene ningún parámetro, declare como si tuviera uno para guardar un byte:

f=()=>"something"
f=x=>"something"

Cuidado: las funciones de flecha no son exactamente las mismas que function () {}. Las reglas para thisson diferentes (y mejor IMO). Ver documentos


2
Pero cuando juegas al golf, generalmente no te importan, thisetc.
Optimizer

1
En general no, pero es una advertencia, una que tal vez nunca sepas cuando surja. También es más común que las lambdas no necesiten una función local de este enlace en la producción.
Isiah Meadows

Además, si desea tomar todos sus argumentos, puede usar la función de argumento "rest", por ejemplo, f=(...x)=>x tendría eso f(1,2,3) => [1,2,3].
Conor O'Brien

1
Aquí hay un consejo específico para este sitio: si está respondiendo con una función que toma la forma (x,y)=>..., puede guardar un byte con curry reemplazándolo conx=>y=>...
Cyoce

12

Usando evalpara funciones de flecha con múltiples declaraciones y unreturn

Uno de los trucos más ridículos que he encontrado ...

Imagine una función de flecha simple que necesita múltiples enunciados y a return.

a=>{for(o="",i=0;i<a;i++)o+=i;return o}

Una función simple que acepta un único parámetro a, que itera sobre todos los enteros [0, a)y los pega al final de la cadena de salida o, que se devuelve. Por ejemplo, llamar a esto 4como el parámetro produciría 0123.

Tenga en cuenta que esta función de flecha tenía que estar envuelta en llaves {}, y tener una return oal final.

Este primer intento pesa 39 bytes .

No está mal, pero al usarlo eval, podemos mejorar esto.

a=>eval('for(o="",i=0;i<a;i++)o+=i;o')

Esta función eliminó las llaves y la declaración de retorno envolviendo el código en un evaly simplemente haciendo la última declaración en la evalevaluación para o. Esto hace evalque regrese o, lo que a su vez hace que la función regrese o, ya que ahora es una sola declaración.

Este intento mejorado pesa 38 bytes , ahorrando un byte del original.

¡Pero espera hay mas! Las declaraciones de evaluación devuelven lo que evaluó su última declaración. En este caso, o+=ievalúa a o, por lo que no necesitamos ;o! (¡Gracias, edc65!)

a=>eval('for(o="",i=0;i<a;i++)o+=i')

Este intento final pesa solo 36 bytes , ¡un ahorro de 3 bytes sobre el original!


Esta técnica podría extenderse a cualquier caso general en el que una función de flecha necesita devolver un valor y tener múltiples declaraciones (que no podrían combinarse por otros medios)

b=>{statement1;statement2;return v}

se convierte

b=>eval('statement1;statement2;v')

guardar un byte

Si se statement2evalúa como v, esto puede ser

b=>eval('statement1;statement2')

ahorrando un total de 3 bytes.


1
Creo que escribir una función anónima podría ser aún más corto
Downgoat

@vihan sí, ambas funciones podrían hacerse anónimas para guardar 2 bytes cada una. Sin embargo, el ahorro de un byte sigue en pie.
jrich

1
Pero aún mejor: eval devuelve la última expresión evaluada, por lo que no necesita ;o- pruébelo:a=>eval('for(o="",i=0;i<a;i++)o+=i')
edc65

44
¡Pero cadenas de plantillas!
Conor O'Brien el

1
@ CᴏɴᴏʀO'Bʀɪᴇɴ ¿Le importaría explicar cómo funcionarían aquí las cadenas de plantilla utilizando la función de ejemplo como contexto?
WallyWest

10

Prefiere cadenas de plantilla nuevas líneas sobre "\ n"

Esto comenzará a dar sus frutos incluso en un nuevo carácter de línea en su código. Un caso de uso podría ser:

(16 bytes)

array.join("\n")

(15 bytes)

array.join(`
`)

Actualización: Incluso puede dejar de lado las llaves debido a las cadenas de plantillas etiquetadas (¡gracias, edc65!):

(13 bytes)

array.join`
`

55
Pero aún mejor, puedes evitar el paréntesis. Lea los documentos ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ) para ver por qué
edc65

Ah bien. Gracias, lo he añadido.
Chiru

9

Relleno de matrices: valores estáticos y rangos dinámicos

Originalmente los dejé como comentarios bajo comprensión, pero dado que esa publicación se centró principalmente en las comprensiones, pensé que sería bueno darle a este su propio lugar.

ES6 nos dio la capacidad de llenar matrices con valores estáticos sin el uso de bucles:

// ES5
function(x){for(i=0,a=[];i<x;i++)a[i]=0;return a}

// ES6
x=>Array(x).fill(0)

Ambos devuelven una matriz de longitud x, llena con el valor 0.

Sin embargo, si desea llenar matrices con valores dinámicos (como un rango de 0 ... x), el resultado es un poco más largo (aunque aún más corto que la forma anterior):

// ES5
function(x){for(i=0,a=[];i<x;i++)a[i]=i;return a}

// ES6
x=>Array(x).fill().map((a,i)=>i)

Ambos devuelven una matriz de longitud x, comenzando con el valor 0 y terminando en x-1.

La razón por la que necesita la entrada .fill()es porque simplemente inicializar una matriz no le permitirá mapearla. Es decir, hacer x=>Array(x).map((a,i)=>i)devolverá una matriz vacía. También puede evitar la necesidad de relleno (y, por lo tanto, hacerlo aún más corto) utilizando el operador de propagación de la siguiente manera:

x=>[...Array(x)]

Usando el operador de propagación y la .keys()función, ahora puede hacer un corto rango de 0 ... x:

x=>[...Array(x).keys()]

Si desea un rango personalizado de x ... y, o un rango especializado por completo (como números pares), puede deshacerse de él .keys()y simplemente usarlo .map()o usarlo .filter()con el operador de propagación:

// Custom range from x...y
(x,y)=>[...Array(y-x)].map(a=>x++)

// Even numbers (using map)
x=>[...Array(x/2)].map((a,i)=>i*2)

// Even numbers (using filter)
x=>[...Array(x).keys()].filter(a=>~a%2)

Aquí hay una sugerencia para el segundo ejemplo: x=>Array(x).fill(i=0).map(a=>i++)Además, no estoy seguro de que el 0 .fill(0)sea ​​necesario. ¿Lo has probado sin?
ETHproductions

@ETHproductions Tienes razón, olvidé que el 0 no es necesario en el relleno antes del mapa. Sin embargo, esto hace que sea 1 carácter más corto que el sugerido, así que lo mantendré así. ¡Gracias!
Mwr247

Además, para el último ejemplo, a=>a%2-1funciona bien, al igual que a=>a%2<1.
ETHproductions

1
Un nuevo truco que he aprendido: [...Array(x)]funciona igual de bien Array(x).fill()y es 2 bytes más corto. x=>[...Array(x)].map((a,i)=>i)
ETHproductions

1
@yonatanmn ¡Muy bien! Solo los comentarios serían 1) el 1/4ejemplo se escribiría más [0,0,0,0]brevemente y 2) las funciones en cadena son específicas de la implementación, por lo que no devolverán una longitud confiable ( Mapes 32 bytes en Chrome, pero 36 bytes en Firefox).
Mwr247

9

Valores devueltos en funciones de flecha

Es de conocimiento común que si una sola declaración sigue la declaración de la función de flecha, devuelve el resultado de esa declaración:

a=>{return a+3}
a=>a+3

-7 bytes

Entonces, cuando sea posible, combine varias declaraciones en una. Esto se hace más fácilmente rodeando las declaraciones con paréntesis y separándolas con comas:

a=>{r=0;a.map(n=>r+=n);return r}
a=>(r=0,a.map(n=>r+=n),r)

-8 bytes

Pero si solo hay dos declaraciones, generalmente es posible (y más corto) combinarlas con &&o ||:

a=>{r=0;a.map(n=>r+=n);return r}

// - Use && because map always returns an array (true)
// - declaration of r moved into unused map argument to make it only 2 statements
a=>a.map(n=>r+=n,r=0)&&r

-9 bytes

Finalmente, si está utilizando un mapa (o similar) y necesita devolver un número y puede garantizar que el mapa nunca devolverá una matriz de 1 longitud con un número, puede devolver el número con |:

a=>{a=b=0;a.map(n=>(a+=n,b-=n));return a/b}

// - {} in map ensures it returns an array of undefined, so the | will make the returned
//   array cast from [ undefined, undefined, undefined ] to ",," to NaN to 0 and 0|n = n,
//   if the map returned [ 4 ] it would cast from [ 4 ] to "4" to 4 and make it 4|n
a=>a.map(n=>{a+=n,b-=n},a=b=0)|a/b

En ese último ejemplo, también debe asegurarse de que el número siempre será un número entero.
ETHproductions

8

Hacks aleatorios de cadenas de plantillas

Esta función rifla dos cadenas (es decir, se convierte "abc","de"en "adbec"):

f=(x,y)=>String.raw({raw:x},...y)

Tenga en cuenta que esto solo funciona cuando xes más largo que y. ¿Cómo funciona, preguntas? String.rawestá diseñado para ser una etiqueta de plantilla, así:

String.raw`x: ${x}\ny: ${y}\nx + y: ${x + y}`

Esto básicamente llama String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y), aunque no es tan simple. La matriz de plantillas también tiene una rawpropiedad especial , que es básicamente una copia de la matriz, pero con las cadenas sin formato. String.raw(x, ...args)básicamente regresa x.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...y así sucesivamente hasta que se xacaben los artículos.

Entonces, ahora que sabemos cómo String.rawfunciona, podemos usarlo para nuestra ventaja:

f=(x,y)=>String.raw({raw:x},...y)                   // f("abc", "de") => "adbec"
f=x=>String.raw({raw:x},...[...x].keys())           // f("abc") => "a0b1c"
f=(x,y)=>String.raw({raw:x},...[...x].fill(y))      // f("abc", " ") => "a b c"

Por supuesto, para el último, f=(x,y)=>x.split``.join(y)es mucho más corto, pero entiendes la idea.

Aquí hay un par de funciones de riffling que también funcionan si xy yson de igual longitud:

f=(x,y)=>String.raw({raw:x.match(/.?/g)},...y)
f=(x,y)=>String.raw({raw:x},...y)+y.slice(-1)  // Only works if x.length == y.length

Puede obtener más información String.raw sobre MDN .


7

Cómo jugar al golf con recursión

La recursión, aunque no es la opción más rápida, es a menudo la más corta. En general, la recursividad es más corta si la solución puede simplificarse a una parte más pequeña del desafío, especialmente si la entrada es un número o una cadena. Por ejemplo, si f("abcd")se puede calcular a partir de "a"y f("bcd"), generalmente es mejor usar la recursividad.

Tomemos, por ejemplo, factorial:

n=>[...Array(n).keys()].reduce((x,y)=>x*++y,1)
n=>[...Array(n)].reduce((x,_,i)=>x*++i,1)
n=>[...Array(n)].reduce(x=>x*n--,1)
n=>{for(t=1;n;)t*=n--;return t}
n=>eval("for(t=1;n;)t*=n--")
f=n=>n?n*f(n-1):1

En este ejemplo, la recursividad es obviamente mucho más corta que cualquier otra opción.

¿Qué tal la suma de los códigos de barras:

s=>[...s].map(x=>t+=x.charCodeAt(),t=0)|t
s=>[...s].reduce((t,x)=>t+x.charCodeAt())
s=>[for(x of(t=0,s))t+=x.charCodeAt()]|t  // Firefox 30+ only
f=s=>s?s.charCodeAt()+f(s.slice(1)):0

Este es más complicado, pero podemos ver que cuando se implementa correctamente, la recursión ahorra 4 bytes .map.

Ahora veamos los diferentes tipos de recursividad:

Pre-recursividad

Este suele ser el tipo de recursión más corto. La entrada se divide en dos partes ay b, y la función calcula algo con ay f(b). Volviendo a nuestro ejemplo factorial:

f=n=>n?n*f(n-1):1

En este caso, aes n , bes n-1 y el valor devuelto es a*f(b).

Nota importante: Todas las funciones recursivas deben tener una forma de detener el recursivo cuando la entrada es lo suficientemente pequeña. En la función factorial, esto se controla con n? :1, es decir, si la entrada es 0 , devuelve 1 sin fvolver a llamar .

Post-recursión

La post-recursión es similar a la pre-recursión, pero ligeramente diferente. La entrada se divide en dos partes ay b, y la función calcula algo con a, luego llama f(b,a). El segundo argumento generalmente tiene un valor predeterminado (es decir f(a,b=1)).

La pre-recursión es buena cuando necesitas hacer algo especial con el resultado final. Por ejemplo, si desea el factorial de un número más 1:

f=(n,p=1)=>n?f(n-1,n*p):p+1

Aun así, sin embargo, post- no siempre es más corto que usar pre-recursión dentro de otra función:

n=>(f=n=>n?n*f(n-1):1)(n)+1

Entonces, ¿cuándo es más corto? Puede notar que la post-recursión en este ejemplo requiere paréntesis alrededor de los argumentos de la función, mientras que la pre-recursión no. Generalmente, si ambas soluciones necesitan paréntesis alrededor de los argumentos, la post-recursión es alrededor de 2 bytes más corta:

n=>!(g=([x,...a])=>a[0]?x-a.pop()+g(a):0)(n)
f=([x,...a],n=0)=>a[0]?f(a,x-a.pop()+n):!n

(Programas tomados de esta respuesta )

Cómo encontrar la solución más corta

Por lo general, la única forma de encontrar el método más corto es probarlos todos. Esto incluye:

  • Bucles
  • .map(para cadenas, ya sea [...s].mapo s.replace; para números, puede crear un rango )
  • Matriz de comprensiones
  • Pre-recursión (a veces dentro de otra de estas opciones)
  • Post-recursión

Y estas son solo las soluciones más comunes; La mejor solución podría ser una combinación de estos, o incluso algo completamente diferente . La mejor manera de encontrar la solución más corta es probarlo todo .


1
+1 por su valor, y me gustaría agregar otro +1 para zootopia
edc65

7

Maneras más cortas de hacer .replace


Si desea reemplazar todas las instancias de una subcadena exacta con otra en una cadena, la forma obvia sería:

f=s=>s.replace(/l/g,"y") // 24 bytes
f("Hello, World!")       // -> "Heyyo, Woryd!"

Sin embargo, puede hacer 1 byte más corto:

f=s=>s.split`l`.join`y`  // 23 bytes
f("Hello, World!")       // -> "Heyyo, Woryd!"

Tenga en cuenta que esto ya no es más corto si desea utilizar cualquier función de expresiones regulares además del gindicador. Sin embargo, si está reemplazando todas las instancias de una variable, generalmente es mucho más corta:

f=(s,c)=>s.replace(RegExp(c,"g"),"") // 36 bytes
f=(s,c)=>s.split(c).join``           // 26 bytes
f("Hello, World!","l") // -> "Heo, Word!"

A veces querrás mapear cada carácter en una cadena, reemplazando cada uno con algo más. A menudo me encuentro haciendo esto:

f=s=>s.split``.map(x=>x+x).join`` // 33 bytes
f=s=>[...s].map(x=>x+x).join``    // 30 bytes
f("abc") // -> "aabbcc"

Sin embargo, .replacecasi siempre es más corto:

f=s=>s.replace(/./g,x=>x+x)  // 27 bytes
f=s=>s.replace(/./g,"$&$&")  // Also works in this particular case

Ahora, si desea mapear cada carácter en una cadena pero no le importa la cadena resultante, .mapgeneralmente es mejor porque puede deshacerse de .join``:

f=s=>s.replace(/./g,x=>t+=+x,t=0)&&t // 36 bytes
f=s=>[...s].map(x=>t+=+x,t=0)&&t     // 32 bytes
f("12345")  // -> 15

Para el último caso, si solo /\w/gestán interesados ciertos caracteres que coinciden con una expresión regular (me gusta ), entonces usar reemplazar será mucho mejor como en esta demostración .
Shieru Asakoto

6

Escribir literales RegEx con eval

El constructor de expresiones regulares puede ser muy voluminoso debido a su nombre largo. En cambio, escriba un literal con eval y backticks:

eval(`/<${i} [^>]+/g`)

Si la variable ies igual a foo, esto generará:

/<foo [^>]+/g

Esto es igual a:

new RegExp("<"+i+" [^>]+","g")

También puede usar String.rawpara evitar tener que escapar repetidamente barras inclinadas\

eval(String.raw`/\(?:\d{4})?\d{3}\d{3}\d{3}\d{3}\d{3}\d{3}\d{4}/g`)

Esto generará:

/(?:\d{4})?\d{3}\d{3}\d{3}/g

Que es igual a:

RegExp("\\(?:\\d{4})?\\d{3}\\d{3}\\d{3}\\d{3}\\d{3}\\d{3}\\d{4}","g")

¡Tenga en cuenta!

String.rawocupa muchos bytes y, a menos que tenga al menos nueve barras invertidas, String.rawserá más largo.


No necesita el newallí, por lo que usar el constructor es realmente más corto para el segundo ejemplo
Optimizer el

5

.forEachvs forbucles

Siempre prefiera .mapcualquier para el bucle. Ahorro fácil e instantáneo.


a.map(f)
for(x of a)f(x);
for(i=0;i<a.length;)f(a[i++]);
  • 8 bytes en total para el original
  • 8 bytes guardados vs para-de ( reducción del 50% )
  • 22 bytes guardados frente al estilo C para el bucle ( reducción del 73% )

a.map(x=>f(x,0))
for(x of a)f(x,0);
for(i=0;i<a.length;)f(a[i++],0);
  • 16 bytes en total para el original
  • 2 bytes guardados vs para-de ( reducción del 11% )
  • 16 bytes guardados frente al estilo C para el bucle ( reducción del 50% )

a.map((x,i)=>f(x,i,0))
for(i in a)f(a[i],i,0);
for(i=0;i<a.length;)f(a[i],i++,0);
  • 22 bytes en total para el original
  • 1 byte guardado frente a for-in ( reducción del 4% )
  • 11 bytes guardados frente al estilo C para el bucle ( reducción del 33% )

a.map(x=>f(x)&g(x))
for(x of a)f(x),g(x);
for(i=0;i<a.length;)f(x=a[i++]),g(x);
  • 19 bytes en total para el original
  • 2 bytes guardados vs para-de ( reducción del 10% )
  • 18 bytes guardados frente al estilo C para el bucle ( reducción del 49% )

5

Uso de contadores no inicializados en recursividad

Nota : Estrictamente hablando, esto no es específico para ES6. Sin embargo, tiene más sentido usar y abusar de la recursividad en ES6, debido a la naturaleza concisa de las funciones de flecha.


Es bastante común encontrar una función recursiva que utiliza un contador kinicialmente establecido en cero e incrementado en cada iteración:

f = (…, k=0) => [do a recursive call with f(…, k+1)]

Bajo ciertas circunstancias, es posible omitir la inicialización de dicho contador y reemplazarlo k+1con -~k:

f = (…, k) => [do a recursive call with f(…, -~k)]

Este truco generalmente ahorra 2 bytes .

¿Por qué y cuándo funciona?

La fórmula que lo hace posible es ~undefined === -1. Entonces, en la primera iteración, -~kse evaluará a 1. En las próximas iteraciones, -~kes esencialmente equivalente a lo -(-k-1)que es igual k+1, al menos para enteros en el rango [0 ... 2 31 -1].

Sin embargo, debe asegurarse de que tener k = undefineden la primera iteración no interrumpirá el comportamiento de la función. Debe tener especialmente en cuenta que la mayoría de las operaciones aritméticas involucradas undefinedresultarán en NaN.

Ejemplo 1

Dado un número entero positivo n, esta función busca el número entero más pequeño kque no se divide n:

f=(n,k=0)=>n%k?k:f(n,k+1)   // 25 bytes

Se puede acortar a:

f=(n,k)=>n%k?k:f(n,-~k)     // 23 bytes

Esto funciona porque n % undefinedes NaN, lo cual es falso. Ese es el resultado esperado en la primera iteración.

[Enlace a la respuesta original]

Ejemplo # 2

Dado un entero positivo n, esta función busca un entero ptal que (3**p) - 1 == n:

f=(n,p=0,k=1)=>n<k?n>k-2&&p:f(n,p+1,k*3)  // 40 bytes

Se puede acortar a:

f=(n,p,k=1)=>n<k?n>k-2&&p:f(n,-~p,k*3)    // 38 bytes

Esto funciona porque pno se usa en absoluto en la primera iteración ( n<ksiendo falso).

[Enlace a la respuesta original]


5

Funciones ES6

Matemáticas

Math.cbrt(x)guarda caracteres que Math.pow(x,1/3).

Math.cbrt(x)
Math.pow(x,1/3)

3 caracteres guardados

Math.hypot(...args)es útil cuando necesitas la raíz cuadrada de la suma de los cuadrados de los argumentos. Hacer que el código ES5 haga eso es mucho más difícil que usar un incorporado.

La función Math.trunc(x)no sería útil, ya que x|0es más corta. (¡Gracias Mwr247!)

Hay muchas propiedades que requieren mucho código en ES5, pero son más fáciles en ES6:

  • Math.acosh, asinh, atanh, cosh, sinh, tanh. Calcula el equivalente hiperbólico de las funciones trigonométricas.
  • Math.clz32. Podría ser posible hacerlo en ES5, pero ahora es más fácil. Cuenta los ceros a la izquierda en la representación de 32 bits de un número.

Hay mucho más, así que sólo voy a enumerar algunas:
Math.sign, Math.fround, Math.imul, Math.log10, Math.log2, Math.log1p.


Math.trunc(x)es cuatro veces más largo que x|0.
Mwr247

@ mwr247: Ok, se actualizará.
ev3commander

Aquí están los equivalentes ES5 más cortos que conozco para un par de estas funciones: Math.hypot(a,b) => Math.sqrt(a*a+b*b)(3 bytes más; se alarga aún más con más argumentos), Math.sign(a) => (a>0)-(a<0)(1 byte más corto, pero necesita paréntesis circundantes en algunos casos; puede no funcionar NaN)
ETHproductions

@ETHproductions Necesita la matriz de argumentos para (la solución alternativa es5 de) hypot. ¿Y está seguro de que la solución para Math.sign funciona para -0? (Debería volver -0)
ev3commander

1
@ ev3commander Estos son solo reemplazos en línea para sus respectivos equivalentes ES6, por lo que se reducen para el 99% de los usos. Volver a crear estas funciones realmente requeriría mucho más código. Además, no veo ninguna razón para necesitar tener un caso especial para -0, ya que (AFAIK) no hay forma de obtener -0 excepto al especificarlo manualmente, y prácticamente no lo utilizo dentro del código de golf. Pero gracias por señalar esas cosas.
ETHproductions

5

Optimización de pequeños rangos constantes para map()

Contexto

map()for[0..N1]

for(i = 0; i < 10; i++) {
  do_something_with(i);
}

puede ser reemplazado por:

[...Array(10).keys()].map(i => do_something_with(i))

o más comúnmente:

[...Array(10)].map((_, i) => do_something_with(i))

Array(N)N

[0..N1]

i

N           | Method                               | Example                         | Length
------------+--------------------------------------+---------------------------------+-------
N ≤ 6       | use a raw array of integers          | [0,1,2,3].map(i=>F(i))          | 2N+10
N = 7       | use either a raw array of integers   | [0,1,2,3,4,5,6].map(i=>F(i))    | 24
            | or a string if your code can operate | [...'0123456'].map(i=>F(i))     | 23
            | with characters rather than integers |                                 |
8 ≤ N ≤ 9   | use scientific notation 1e[N-1]      | [...1e7+''].map((_,i)=>F(i))    | 24
N = 10      | use scientific notation 1e9          | [...1e9+''].map((_,i)=>F(i))    | 24
            | or the ES7 expression 2**29+'4' if   | [...2**29+'4'].map(i=>F(i))     | 23
            | the order doesn't matter and your    |                                 |
            | code can operate with characters     |  (order: 5,3,6,8,7,0,9,1,2,4)   |
            | rather than integers                 |                                 |
11 ≤ N ≤ 17 | use scientific notation 1e[N-1]      | [...1e12+''].map((_,i)=>F(i))   | 25
N = 18      | use the fraction 1/3                 | [...1/3+''].map((_,i)=>F(i))    | 24
N = 19      | use the fraction 1/6                 | [...1/6+''].map((_,i)=>F(i))    | 24
20 ≤ N ≤ 21 | use scientific notation 1e[N-1]      | [...1e20+''].map((_,i)=>F(i))   | 25
N = 22      | use scientific notation -1e20        | [...-1e20+''].map((_,i)=>F(i))  | 26
23 ≤ N ≤ 99 | use Array(N)                         | [...Array(23)].map((_,i)=>F(i)) | 27

NB : no se cuenta la longitud del código de devolución de llamada F(i).

[1..9]

[1..9]

[...17**6+'8'].map(i=>F(i))  // order: 2,4,1,3,7,5,6,9,8; length: 23

Optimizaciones sin contador

N

N           | Method                               | Example                         | Length
------------+--------------------------------------+---------------------------------+-------
N ≤ 5       | use a raw array of integers          | [0,0,0,0].map(_=>F())           | 2N+10
6 ≤ N ≤ 10  | use scientific notation 1e[N-1]      | [...1e7+''].map(_=>F())         | 20
11 ≤ N ≤ 17 | use scientific notation 1e[N-1]      | [...1e12+''].map(_=>F())        | 21
N = 18      | use the fraction 1/3                 | [...1/3+''].map(_=>F())         | 20
N = 19      | use the fraction 1/6                 | [...1/6+''].map(_=>F())         | 20
20 ≤ N ≤ 21 | use scientific notation 1e[N-1]      | [...1e20+''].map(_=>F())        | 21
N = 22      | use scientific notation -1e20        | [...-1e20+''].map(_=>F())       | 22
23 ≤ N ≤ 99 | use Array(N)                         | [...Array(23)].map(_=>F())      | 23

NB : no se cuenta la longitud del código de devolución de llamada F().


No debe 2**26ser 2**29?
Shaggy

@Shaggy Heck. ¡Buena atrapada!
Arnauld

¡No quería editar en mí porque tengo ceguera al código! : D
Shaggy

Usando .keys(), no necesitas un lambda:[...Array(10).keys()].map(do_something_with)
long-lazuli

@ long-lazuli Si no necesitas una lambda y solo quieres un rango, entonces probablemente tampoco necesites un mapa ...
Arnauld

4

Desestructuración de tareas

ES6 presenta una nueva sintaxis para las tareas de desestructuración, es decir, cortar un valor en piezas y asignar cada pieza a una variable diferente. Aquí están algunos ejemplos:

Cadenas y matrices

a=s[0];b=s[1];       // 14 bytes
[a,b]=s;             //  8 bytes

a=s[0];s=s.slice(1); // 20 bytes
a=s.shift();         // 12 bytes, only works if s is an array
[a,...s]=s;          // 11 bytes, converts s to an array

Objetos

a=o.asdf;b=o.bye;c=o.length; // 28 bytes
{asdf:a,bye:b,length:c}=o;   // 26 bytes

a=o.a;b=o.b;c=o.c; // 18 bytes
{a,b,c}=o;         // 10 bytes

Estas asignaciones también se pueden usar en parámetros de función:

f=a=>a[0]+a[1]+a[2]
f=([a,b,c])=>a+b+c

f=b=>b[1]?b[0]+f(b.slice(1)):b[0]*2
f=b=>b[1]?b.shift()+f(b):b[0]*2
f=([a,...b])=>b[0]?a+f(b):a*2

4

Otra forma de evitar return

Sabes que debes usar eval para las funciones de flecha con múltiples declaraciones y un retorno . En algunos casos inusuales, puede ahorrar más utilizando una subfunción interna.

Digo inusual porque

  1. El resultado devuelto no debe ser la última expresión evaluada en el bucle.

  2. Debe haber (al menos) 2 inicializaciones diferentes antes del ciclo

En este caso, puede utilizar una subfunción interna sin retorno, pasando uno de los valores iniciales como parámetro.

Ejemplo Encuentre el recíproco de la suma de la función exp para valores en un rango de a a b.

El largo camino - 55 bytes

(a,b)=>{for(r=0,i=a;i<=b;i++)r+=Math.exp(i);return 1/r}

Con eval - 54 bytes

(a,b)=>eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i);1/r")

Con una función interna - 53 bytes

(a,b)=>(i=>{for(r=0;i<=b;i++)r+=Math.exp(i)})(a)||1/r

Tenga en cuenta que sin el requisito de un límite de rango inferior a, puedo fusionar las inicializaciones de i y r y la versión de evaluación es más corta.


En su muestra no hay necesidad de guardara
l4m2 el

@ l4m2 No puedo entender tu punto, ayuda por favor ...
edc65

(i,b)=>{for(r=0;i<=b;i++)r+=Math.exp(i);return 1/r}
l4m2

@ l4m2 uh bien, return a/rsería un mejor ejemplo
edc65

1
eval aún es mejor (a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")y en este caso(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
JayXon

4

Uso de la sintaxis de curry para funciones diádicas y recursivas

Funciones diádicas

Cada vez que una función toma exactamente dos argumentos sin valores predeterminados, el uso de la sintaxis de curry guarda un byte.

antes de

f =
(a,b)=>a+b  // 10 bytes

Llamado con f(a,b)

Después

f =
a=>b=>a+b   // 9 bytes

Llamado con f(a)(b)

Nota : Esta publicación en Meta confirma la validez de esta sintaxis.

Funciones recursivas

El uso de la sintaxis de curry también puede ahorrar algunos bytes cuando una función recursiva toma varios argumentos, pero solo necesita actualizar algunos de ellos entre cada iteración.

Ejemplo

La siguiente función calcula la suma de todos los enteros en el rango [a,b]:

f=(a,b)=>a>b?0:b+f(a,b-1)   // 25 bytes

Debido a que apermanece sin cambios durante todo el proceso, podemos guardar 3 bytes usando:

f =                         // no need to include this assignment in the answer anymore
a=>F=b=>a>b?0:b+F(b-1)      // 22 bytes

Nota : Como notó Neil en los comentarios, el hecho de que un argumento no se pase explícitamente a la función recursiva no significa que deba considerarse inmutable. Si es necesario, podríamos modificar adentro del código de función con a++, a--o cualquier sintaxis similar.


El último ejemplo se puede escribir como a=>F=b=>a>b?0:a+++F(b), modificando apara cada llamada recursiva. Esto no ayuda en ese caso, pero podría guardar bytes en casos con más argumentos.
Neil

Heh, estaba pensando en escribir un consejo para esto :-)
ETHproductions

4

Función de prueba de primalidad

La siguiente función de 28 bytes devuelve truenúmeros primos y números falseno primos:

f=(n,x=n)=>n%--x?f(n,x):x==1

Esto se puede modificar fácilmente para calcular otras cosas. Por ejemplo, esta función de 39 bytes cuenta el número de primos menor o igual que un número:

f=(n,x=n)=>n?n%--x?f(n,x):!--x+f(n-1):0

Si ya tiene una variable nque desea verificar para primalidad, la función de primalidad se puede simplificar bastante:

(f=x=>n%--x?f(x):x==1)(n)

Cómo funciona

f = (         // Define a function f with these arguments:
  n,          //   n, the number to test;
  x = n       //   x, with a default value of n, the number to check for divisibility by.
) =>
  n % --x ?   //   If n is not divisible by x - 1,
  f(n, x)     //     return the result of f(n, x - 1).
              //   This loops down through all numbers between n and 0,
              //     stopping when it finds a number that divides n.
  : x == 1    //   Return x == 1; for primes only, 1 is the smallest number
              //     less than n that divides n.
              //   For 1, x == 0; for 0, x == -1.

Nota: Esto fallará con un error de "demasiada recursividad" cuando se llama con una entrada suficientemente grande, como 12345. Puede solucionar esto con un bucle:

f=n=>eval('for(x=n;n%--x;);x==1')

1
Pero fallar con demasiada recursividad para una entrada tan pequeña como 12345
edc65

x==1Probablemente sea x<2para ahorrar.
CalculatorFeline

@CalculatorFeline Gracias, pero luego falla por 1o 0(porque xsería 0o -1, respectivamente)
ETHproductions

Podría ser útil en ciertos casos. Además, !~-xpara -0 bytes.
CalculatorFeline

3

Array#concat() y el operador de propagación

Esto depende en gran medida de la situación.


Combinando múltiples matrices.

Prefiere la función concat a menos que la clonación.

0 bytes guardados

a.concat(b)
[...a,...b]

3 bytes desperdiciados

a.concat(b,c)
[...a,...b,...c]

3 bytes guardados

a.concat()
[...a]

6 bytes guardados

// Concatenate array of arrays
[].concat.apply([],l)
[].concat(...l)

Prefiere usar una matriz ya existente para Array#concat().

Fácil 4 bytes guardados

[].concat(a,b)
a.concat(b)

3

Devuelve el resultado intermedio

Sabe que con el operador de coma puede ejecutar una secuencia de expresiones que devuelven el último valor. Pero al abusar de la sintaxis de la matriz literal, puede devolver cualquier valor intermedio. Es útil en .map () por ejemplo.

// capitalize words
// f is a flag indicating if prev char is space
[...x].map(c=>(f?c=c.toUpperCase():0,f=c<'!',c),f=1).join('')

// shortened to ...
[...x].map(c=>[f?c.toUpperCase():c,f=c<'!'][0],f=1).join('')

3
Recuerde, por supuesto, que .join('')puede ser.join``
Cyoce

3

Establecer valores predeterminados de parámetros de función

($,a,b,_)=>_!=undefined?'asdf':_ // before
($,a,b,_)=>_!=[]._?'asdf':_ // before, but a bit golfed
($,a,b,_='asdf')=>_ // after

Este es realmente útil ...

Sin embargo, asegúrese de comprender que algo así _=>_||'asdf'es más corto cuando solo pasa un argumento (útil) a la función.


1
Me gustaría señalar que el uso de un OR _=>_||'asdf'generalmente es más corto en la mayoría de los casos
Downgoat

@Downgoat Notaría que eso devuelve "asdf"una entrada de ""(cadena vacía).
ETHproductions

2
Tenga en cuenta que el valor predeterminado se evalúa siempre que el argumento hubiera sido undefined, incluso si pasa explícitamente ese valor. Por ejemplo, [...Array(n)].map((a,b,c)=>b)siempre pasa undefinedpor a, y por lo tanto puede proporcionarle un valor predeterminado (aunque no en términos de b).
Neil

3

Use en evallugar de llaves para las funciones de flecha

Las funciones de flecha son increíbles. Toman la forma x=>y, donde xes un argumento y yes el valor de retorno. Sin embargo, si necesita usar una estructura de control, como while, tendría que poner llaves, por ejemplo =>{while(){};return}. Sin embargo, podemos evitar esto; Afortunadamente, la evalfunción toma una cadena, evalúa esa cadena como código JS y devuelve la última expresión evaluada . Por ejemplo, compare estos dos:

x=>{while(foo){bar};return baz} // before
x=>eval('while(foo){bar};baz')  // after
//                            ^

Podemos usar una extensión de este concepto para acortar aún más nuestro código: a los ojos de eval, las estructuras de control también devuelven su última expresión evaluada. Por ejemplo:

x=>{while(foo)bar++;return bar} // before
x=>eval('while(foo)++bar')      // after
//                        ^^^^^

3

Operaciones lógicas de golf en ES6

"GLOE (S6)"

Lógica general

Digamos que ha construido declaraciones sy t. Vea si puede usar alguno de los siguientes reemplazos:

Traditional conjuction: s&&t
Equivalent conjuction: s*t OR s&t

Traditional disjunction: s||t
Equivalent disjunction: s+t OR s|t

(Es posible que no funcionen si el orden es incorrecto; es decir, +y *tienen una prioridad de orden menor que ||y &&).

Además, aquí hay algunas expresiones lógicas útiles:

  • Ya sea so tes verdad / XOR:s^t
  • sy tson el mismo valor de verdad: !s^tos==t

Lógica de matriz

Todos los miembros de asatisfacer condición p:

a.every(p)                             // 10 bytes (11 bytes saved)
a.map(x=>c&=p(x),c=1)                  // 21 bytes (16 bytes saved)
for(i=0,c=1;i<a.length;c&=p(a[i++]));  // 37 bytes (hideously long)

Al menos un miembro de la acondición satisface p:

a.some(p)                            // 9  bytes (13 bytes saved)
a.map(x=>c|=p(x),c=0)                // 21 bytes (14 bytes saved)
for(i=c=0;i<a.length;c|=p(a[i++]));  // 35 bytes (just please no)

Ningún miembro de ala condición satisfacen p: !a.some(p).

El elemento eexiste en la matriz a:

a.includes(e)                        // 13 bytes, standard built-in
~a.indexOf(e)                        // 13 bytes, "traditional" method
a.find(x=>e==x)                      // 15 bytes, find (ES6)
a.some(x=>x==e)                      // 15 bytes, some (ES5)
(a+"").search(e)                     // 16 bytes, buggy
a.filter(t=>t==e).length             // 24 bytes, no reason to use this
for(i=c=0;i<a.length;c+=e==a[i++]);  // 35 bytes, super-traditional

Elemento eno no existe en array a:

!a.includes(e)
!~a.indexOf(e)
a.every(t=>t!=e)
!a.filter(t=>t==e).length
for(i=0,c=1;i<a.length;c*=e!=a[i++]);

Generalmente uso &&y ||como x?y:xy x?x:y, respectivamente. Pero puedo ver cómo esto sería útil en más programas basados ​​en la lógica. El único problema con +sería que por ejemplo, 3y -3son a la vez Truthy, pero 3+-3no lo es.
ETHproductions

@ETHproductions Ah, tienes razón; Ese es un caso extremo. -También podría funcionar, si s != t.
Conor O'Brien

a.filter(t=>t==e).length==a.lengthEs incorrecto. Debería ser!a.filter(t=>t==e).length
ETHproductions

@ETHproductions tiene razón!
Conor O'Brien el

3

Acortar llamadas a funciones repetidas

Si ha repetido llamadas a una función con un nombre largo, como la manipulación del lienzo:

c.lineTo(0,100);c.lineTo(100,100);c.lineTo(100,0);c.lineto(0,0);c.stroke()

La forma tradicional de acortarlo sería alias el nombre de la función:

c[l='lineTo'](0,100);c[l](100,100);c[l](100,0);c[l](0,0);c.stroke()

Si tiene suficientes llamadas, una mejor manera es crear una función que haga el trabajo por usted:

l=(x,y)=>c.lineTo(x,y);l(0,100);l(100,100);l(100,0);l(0,0);c.stroke()

Si la mayoría de las llamadas a funciones están encadenadas, puede hacer que la función se devuelva, lo que le permite cortar dos bytes de cada llamada sucesiva:

l=(x,y)=>c.lineTo(x,y)||l;l(0,100)(100,100)(100,0)(0,0);c.stroke()

Ejemplo de uso: 1 , 2


1
puede acortar con el operador de enlace :(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
Downgoat

@Downgoat Gracias, ¿qué navegadores soportan eso? (Además, por lo que he visto, se producirá un error en la segunda llamada, ya c.lineToque no se devuelve naturalmente)
ETHproductions

tienes que frotarlo a través de babel ya que es una función
ES7

3

Operador de enlace ::

El operador de enlace se puede usar para ayudar a acortar bytes sobre funciones repetidas:

(x='abc'.search(a))+x.search(b) // Before
(x=::'abc'.search)(a)+x(b)      // 5 bytes saved

Además, si desea utilizar la función con un thisejemplo diferente, por ejemplo:

s[r='replace'](/a/g,'b')+s[r](/c/g,'d') // Before
(r=s.replace)(/a/g,'b')+s::r(/c/g,'d')  // 1 byte saved

3

Evitar comas al almacenar muchos datos

Si tiene muchos datos (es decir, índices, caracteres, ...) que necesita almacenar en una matriz, es mejor que deje todas las comas. Esto funciona mejor si cada dato tiene la misma longitud de cadena, 1 obviamente es óptimo.

43 bytes (línea de base)

a=[[3,7,6,1,8,9,4,5,2],[5,4,3,2,7,6,5,4,3]]

34 bytes (sin comas)

a=[[..."376189452"],[..."543276543"]]

Si está dispuesto a cambiar su acceso a la matriz , puede reducir esto aún más, almacenando los mismos valores de la siguiente manera:

27 bytes (mismos datos, solo cambia el acceso a la matriz)

a=[..."376189452543276543"]

¿Por qué solo se resalta el último bloque?
CalculatorFeline

@CalculatorFeline Gracias, arreglado.
Chiru
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.