JavaScript
Node.js
ワンライナー
シェル芸
JSシェル芸

私は普段、色々な処理をJavascript1行で書くことを趣味にしていて、過去いろいろな処理を1行で書いています。
通称「JSシェル芸」「Javascriptシェル芸」などのアダ名で呼ばれています。
最初に披露したのが第28回基準値を超えるシェル芸勉強会で、そこで好評だったために細々と続けています。
最近はシェル芸勉強会以外にも、Javascript関連の勉強会などのLTでやったりして、そこそこウケたり、ドン引きされたりしています。
他に書いている人がおらず、『どうやって作成しているのですか?』という質問をもらったりすることもあり、今回は私の作成の仕方をまとめたいと思います。

作成例

素数の算出

console.log(((num = 60, f = (arr, i) => arr[i] > Math.sqrt(num) ? arr : f(arr.filter((v) => v === arr[i] || v % arr[i] !== 0), ++i)) => f([...Array(num)].map((v,i) => i+1).slice(1), 0)).call())

sin波を書く

[...Array(20)].map((u, i) => Math.round((Math.sin(i) + 1) * 4)).forEach((count) => console.log(`${ " ".repeat(count) }*`))

シェルピンスキーのギャスケット
sierpinski_gasket.gif

https://github.com/butackle/JavascriptOneLiner/blob/master/monsterLine/sierpinski_gasket.js

マイルール

セミコロンを使わず全て繋げて書くというのが大原則です。
セミコロンを使うと、ただの圧縮になってしまうので・・・。
あと最近は、{}も使わずに書くようにしています。アロー関数を繋げまくります。
実行環境は特に決めていません。
ブラウザのコンソールを使う時もあれば、ファイル読込とかが必要な際にはNode.jsを使ったりしますし、図を描画したい時には下記のHTMLファイルを使う時もあります。

canvas.html
<canvas id=canvas width="2000px" height="2000px"></canvas>

<html>とか<head>とかはもう面倒なので省いています。

作成の仕方

慣れてくると、徐々に最初から1行では書けるようにはなるのですが、
悩む場合は、最初から繋げて書いたりはしません。
部品ごとに作っていきます。

/*
響け!ユーフォニアム
け!ユーフォニアム響
!ユーフォニアム響け
ユーフォニアム響け!
ーフォニアム響け!ユ
フォニアム響け!ユー
ォニアム響け!ユーフ
ニアム響け!ユーフォ
アム響け!ユーフォニ
厶響け!ユーフォニア
*/

const splitEuphonium = "響け!ユーフォニアム".split("");
const makeLine = (char, index, line) => line.map((str, i, a) => a[Math.abs(i - index)]).join('');
const makeSentence = splitEuphonium.map(makeLine).join('\n')
console.log(makeSentence)

上記の例はシェル芸の王道である響け!ユーフォニアムの回答の一例です。 1行回答としてはconsole.log("響け!ユーフォニアム".split("").map((char, index, line) => line.map((str, i, a) => a[Math.abs(i - index)]).join('')).join('\n'))としていますが、考える過程で
・splitEuphonium → 文字列を分割する処理
・makeLine → 行を作成
・makeSentence → 文章を作成
というように必要な処理を細分化して書いて、そのあとに結合します。
このようにすると、デバッグも楽です。
繋げてからのデバッグは大変だと思います。

制御フロー文を再現してみる

if文

if(Date.now() % 3 === 0) {
    console.log("1");
} else if (Date.now() % 2 === 0) {
    console.log("2");
} else {
    console.log("3");
}
//
// ↑ 同じ出力をする ↓
//
console.log(Date.now() % 3 === 0 ? "1" : Date.now() % 2 === 0 ? "2" : "3")

三項演算子を使います。

for文

for(i=0; i<2; ++i){
    console.log(i);
}
//
// ↑ 同じ出力をする ↓
//
[...Array(2)].forEach((v,i) => console.log(i))

[...Array(2)]で要素数が2の配列を生成しています。

do-while文

i = 0;
do{
    console.log(i++);
}while(i <= 10)
//
// ↑ 同じ出力をする ↓
//
((f = (i) => console.log(i) || i < 10 ? f(++i) : undefined) => f(0)).call()

console.log(i)は必ずundefinedが返るのでfalseとして判定されます。
そうすると必ず次のi<10以降の三項演算子の処理は行われます。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_Operators

作成に使うことが多いメソッド

処理を繋げていくに当たって、通常は配列または文字列を繋いでいくのが基本形です。

Array.prototype.map()

もっとも基本の処理です。この関数を用いて、値を変えながら作成していくことがポピュラーなやり方です。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Array.prototype.reduce()

いろいろと応用が効き便利です。配列からオブジェクト・文字列を作成することも出来ます。
ただreduceの中でreduceを入れると、結構自分の頭で追うのが大変です。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

Array.prototype.join()
String.prototype.split()

配列から文字列を生成したり、文字列を分割したりして、処理を繋げていきます。
他の方法で出来る場合もありますが、こちらは可読性が高いので、初めての方にはオススメです。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/join
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/split

String.prototype.replace()

パターンマッチに正規表現・変換に関数を使うことにより、かなり色々なことが出来ます。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replace

最後に

私自身は、特にJSに詳しいとかいう人間でもなく、今まで書いた事柄は、自分で処理を書きながらMozillaを読んだり試してみたりして把握していったものが大半です。
なるべく誤解や間違いが無いよう書いたつもりですが、もし間違い等ありましたら指摘していただけると嬉しいです。

過去の作成物など