配列からテーブルを作る2

配列からテーブルを作る2

配列からテーブルを作る場合、セルを結合したりclassName を指定したい時がある。そこで配列のセルに該当するテキストから colSpan , rowSpan などを取り出せるようにした。

  1. 文字列の先頭が "#" ならば TH 要素になる
  2. ”!r4c2 def" ならば rowSpan=4, colspan=2, textContent = "def" となる
  3. "!{"className":"red"} ghi ならば class="red", textContent = "ghi" となる。 { } で囲まれる部分は、JSON 形式に変換されるのでダブルクォーテーションを適材適所に使用すること。また {"innerHTML":"<a href='xxx'>Test</a>"}とすれば子要素以降も文字列から生成できる。その場合は適切に文字をエスケープすること。

let ary = [
  ["#!r4{"className":"red"} abc", "#!c4 def, ... ],
  [...],
  ... 
]
function ary2thead (ary = [ ], thead = document.createElement ('thead')) {
  for (let row of ary)
    thead.insertRow().append(...row.map(cell=>{
      let [, th, rowSpan, colSpan, json, textContent = ''] = (new RegExp ('^(#)?(?:!(?=[rc\\{])(?:r([2-9]|[1-9]\\d+)|r[01]|r0\\d+)?(?:c([2-9]|[1-9]\\d+)|c[01]|c0\\d+)?({.+})?)?\\s*(.*)\\s*$','g')).exec (cell) ?? [ ];
      return Object.assign (document.createElement (th ? 'th': 'td'),  Object.entries ({rowSpan, colSpan, textContent}).reduce((a,[b,c])=>((c===undefined||(a[b]=c)),a), {}), json ? JSON.parse(json): {});
    }));
  return thead;
}

短く

const
  ary2thead= (a=[],b=document.createElement('thead'))=>{
  for(let c of a)b.insertRow().append(...c.map(d=>{
  let[,e,f,g,h,i='']=(new RegExp('^(#)?(?:!(?=[rc\\{])(?:r([2-9]|[1-9]\\d+)|r[01]|r0\\d+)?(?:c([2-9]|[1-9]\\d+)|c[01]|c0\\d+)?({.+})?)?\\s*(.*)\\s*$','g')).exec(d)??[];
  return Object.assign(document.createElement(e?'th':'td'),Object.entries({rowSpan:f,colSpan:g,textContent:i}).reduce((j,[k,l])=>((l===undefined||(j[k]=l)),j),{}),h?JSON.parse(h):{});
  }));return b};

文字列からテーブルを作る

配列からテーブルを作る

<!DOCTYPE html>
<meta charset="utf-8">
<title></title>

<style>
  .red { color: red;}
</style>

<body>
<table border="1" id="TB"></table>


<script>


function ary2thead (ary = [ ], thead = document.createElement ('thead')) {

  const
    textSplitStr = (
        th = '(#)?', //文字列の先頭が "#" なら th要素とするための判別
        rowSpan = '(?:r([2-9]|[1-9][0-9]+)|r[01]|r0[0-9]+)?',//rowSpan の2以上を対象として取得
        colSpan = '(?:c([2-9]|[1-9][0-9]+)|c[01]|c0[0-9]+)?',//colSpan の2以上を対象として取得
        attr = '(?:\\{(.+)?\\})?',//attribute として { ... } 括弧の内側を文字列として取得
        text = '\\s*(.*)\\s*', //前後の空白を取り除いて取得
        ctrlCom = '(?:!(?=[rc\\{])'+ rowSpan + colSpan + attr + ')?'
      )=> '^' + th + ctrlCom + text + '$',
    
    attrSplitStr = '\\s*(.+?)\\s*\\:\\s*(.*?)\\s*(?:,|;|$)',
    rows = [ ];


  for (let row of ary) {
    let tds = [];
    for (let cell of row) {
      let
        splitter = new RegExp (textSplitStr (), 'gi');
        [, th, rowSpan, colSpan, attr, textContent = ''] = splitter.exec (cell) ?? [ ],
        prop = new Map;

      if (rowSpan)
        prop.set ('rowSpan', rowSpan);
      if (colSpan)
        prop.set ('colSpan', colSpan);
      
      if (attr)
        for (let a, splitter = new RegExp (attrSplitStr, 'gi'); a = splitter.exec (attr);  )
            prop.set (a[1],a[2]);

      prop.set ('textContent', textContent);

      tds.push (Object.assign (document.createElement (th ? 'th': 'td'), Object.fromEntries (prop)));
    }
    rows.push (tds);
  }

  rows.reduce ((a, b)=>(a.insertRow ().append (...b), a), thead);
  return thead;
}


const RECS = `

#!R2C1 No	#!r2 Name	#!r1c2 INFO
#Age	#ETC

#1	akio	57	abc
#!{id:sss,className: red}2	ayako	56	def



 `;

const csv2ary = csv => csv.split (/r\n|\r|\n/g).reduce ((a,b)=>b.trim()?[...a,b]:a,[]).map (row=> row.split (/\t/g));

ary2thead (csv2ary (RECS), TB);

</script>

ショートコーディングしてみる

<!DOCTYPE html>
<meta charset="utf-8">
<title></title>

<style>
  .red { color: red;}
</style>

<body>
<table border="1" id="TB"></table>


<script>


function ary2tbody (recs = [ ], tbody = document.createElement ('tbody')) {
  function* splitter(a,b=new RegExp(attrSplitStr,'g'),c){while(c=b.exec(a))yield c.slice(1)}
  const
    addObj = (a,[b,c])=>(a[b]=c,a),
    delObj = (a,[b,c])=>((undefined===c)&&delete a[b],a),
    makeTD = (a)=>{
      let
        [, th, rowSpan, colSpan, attr, textContent] = (new RegExp (textSplitStr, 'i')).exec (a) ?? [ ],//文字列を分解
      prop = [...splitter (attr)].reduce (addObj, { rowSpan, colSpan, textContent });//要素の属性

      return Object.assign (
        document.createElement (th ? 'th': 'td'), //フラグとしての th により生成
        Object.entries (prop).reduce (delObj, prop) // prop オブジェクトから値が無いものは削除する 
      )
    },
    attrSplitStr = '\\s*(.+?)\\s*\\:\\s*(.*?)\\s*(?:,|;|$)',//文字列から属性値を切り出す "key:value, key2:value2, ..."
    textSplitStr = //#!r2c3{className:red} text
    '^' + '(#)?' + //th 文字列の先頭が "#" なら th要素とするための判別
    '(?:!(?=[rc{])'+ //制御文字は !で始まり以下の順で記述する
        '(?:r([2-9]|[1-9][0-9]+)|r[01]|r0[0-9]+)?' + //rowSpan の2以上を対象として取得
      '(?:c([2-9]|[1-9][0-9]+)|c[01]|c0[0-9]+)?' + //colSpan の2以上を対象として取得
      '(?:\\{(.+)?\\})?' + //{attr: 123, attr2: 456} の用に属性を取得する
    ')?' +
    '\\s*(.*)\\s*' + '$';//textContent 前後の空白を取り除いて取得

  recs.forEach (row=> tbody.insertRow ().append (...row.map (makeTD)));
  return tbody;
}


const RECS = `

#!R2C1 No  #!r2 Name  #!r1c2 INFO
#Age  #ETC

#1  akio  57  abc
#!{id:sss,className: red}2  ayako  56  def



 `;

const csv2ary = csv => csv.split (/r\n|\r|\n/g).reduce ((a,b)=>b.trim()?[...a,b]:a,[]).map (row=> row.split (/\t/g));

ary2thead (csv2ary (RECS), TB);

</script>

さらにショート!

<!DOCTYPE html>
<meta charset="utf-8">
<title></title>

<style>
  .red { color: red;}
</style>

<body>
<table border="1" id="TB"></table>


<script>
const ary2tbody=(a=[],b=document.createElement('tbody'))=>{
function*c(a,b=new RegExp('\\s*(.+?)\\s*\\:\\s*(.*?)\\s*(?:,|;|$)','g'),c){while(c=b.exec(a))yield c.slice(1)}
const d=(a,[b,c])=>(a[b]=c,a),e=(a,[b,c])=>((undefined===c)&&delete a[b],a),f=g=>{let[,h,i,j,k,l]=(new RegExp('^(#)?(?:!(?=[rc{])(?:r([2-9]|[1-9][0-9]+)|r[01]|r0[0-9]+)?(?:c([2-9]|[1-9][0-9]+)|c[01]|c0[0-9]+)?(?:\\{(.+)?\\})?)?\\s*(.*)\\s*$','i')).exec(g)??[],m=[...c(k)].reduce(d,{rowSpan:i,colSpan:j,textContent:l});return Object.assign(document.createElement(h?'th':'td'),Object.entries(m).reduce(e,m))};
a.forEach(c=>b.insertRow().append(...c.map(f)));return b}


const RECS = `

#!R2C1 No	#!r2 Name	#!r1c2 INFO
#Age	#ETC

#1	akio	57	abc
#!{id:sss,className: red}2	ayako	56	def



 `;

 const csv2ary = csv => csv.split (/r\n|\r|\n/g).reduce ((a,b)=>b.trim()?[...a,b]:a,[]).map (row=> row.split (/\t/g));

ary2tbody (csv2ary (RECS), TB);


</script>

再考して更に短く

const
  ary2tbody=(a=[],b=document.createElement('tbody'))=>{
  let A='^(#)?(?:!(?=[rc\\{])(?:r([2-9]|[1-9]\\d+)|r[01]|r0\\d+)?(?:c([2-9]|[1-9]\\d+)|c[01]|c0\\d+)?(?:\\{(.+)?\\})?)?\\s*(.*)\\s*$',B=RegExp,C=Object;  
  a.map(c=>{b.insertRow().append(...c.map(d=>{let[,z,y,x,w,v]=(new B(A,'i')).exec(d)??[],u={rowSpan:y,colSpan:x,textContent:v};
  C.entries(u).map(a=>a[1]===undefined&&delete u[a[0]]);for(let a,b=new B('\\s*(.+?)\\s*\\:\\s*(.*?)\\s*(,|;|$)','g');a=b.exec(w);u[a[1]]=a[2]);
  return C.assign(document.createElement(z?'th':'td'),u)}))});return b};

javascriptで行列の演算をする

class Matrix {
  constructor (ary) { this.matrix = ary }
  add (arg) { this.matrix = this.constructor.add (this.matrix, arg.matrix) }
  sub (arg) { this.matrix = this.constructor.sub (this.matrix, arg.matrix) }
  mult (arg) { this.matrix = this.constructor.mult (this.matrix, arg.matrix) }
  get transpose () { return this.constructor.transpose (this.matrix) }
  get inverse () { return this.constructor.inverse (this.matrix) }


  //行列の加算        
  static add (a, b) { return a.map((a,i)=> b[i].map((b,j)=> a[j]+b)) }
  //行列の減算
  static sub (a, b) { return a.map((a,i)=> b[i].map((b,j)=> a[j]-b)) }
  //行列の積
  static mult (a, b) { return a.map(a=> a.map((_, i)=> b.reduce((g,_,j)=> g + a[j]*b[j][i],0))) }
  //転置
  static transpose (a) { return a[0].map((_,i)=> a.map ((_,j)=> a[j][i]))}
  //単位行列
  static identity (n) { return [...Array(n)].fill(0).map((a,b,c,d=[...c])=>(d[b]=1,d)) }
  //逆行列
  static inverse (A) { return A.reduce((a,b,c,d,e=b[c],f=a[c])=>A.reduce((a,h,i,_,j=h[c],k=A[i],l=a[i])=>(c==i||A.forEach((_,m)=>(k[m]-=b[m]*j,l[m]-=f[m]*j)),a),(A.forEach((_,i)=>(b[i]/=e,f[i]/=e)),a)),this.identity(A.length))}
}

//_______________

let mt0 = [
  [ 1, 2, 0,-1],
  [-1, 1, 2, 0],
  [ 2, 0, 1, 1],
  [ 1,-2,-1, 1]
];
let rst = Matrix.inverse (mt0);

console.log(rst);



2*2

/*
| a b |
| c d |
*/
class Matrix22 {
  #matrix=new Array(4);
  constructor(A=1,B=0,C=0,D=1){this.#matrix=[A,B,C,D]}
  add(M){let[A,B,C,D]=this.#matrix,[a,b,c,d]=M.matrix;this.#matrix=[A+a,B+b,C+c,D+d];return this}//加算
  sub(M){let[A,B,C,D]=this.#matrix,[a,b,c,d]=M.matrix;this.#matrix=[A-a,B-b,C-c,D-d];return this}//減算
  scalarMultiple(n=1){let[A,B,C,D]=this.#matrix;this.#matrix=[A*n,B*n,C*n,D*n];return this}//スカラー倍
  multiply(M){let[A,B,C,D]=this.#matrix,[a,b,c,d]=M.matrix;this.#matrix=[A*a+B*c,A*b+B*d,C*a+D*c,C*b+D*d];return this}//乗算
  get matrix(){return this.#matrix}//内部の値を配列で返す
  get det(){let[A,B,C,D]=this.#matrix;return A*D-B*C}//行列式(determinant)
  get transpose(){let[A,B,C,D]=this.#matrix;return new this.constructor(A,C,B,D)}//転置
  get inverse(){let[A,B,C,D]=this.#matrix,e=1/this.det;return new this.constructor(D*e,-B*e,-C*e,A*e)}//逆数
}

/*
| a b c | 
| d e f | 
| g h i | 
*/
class Matrix33 {
  #matrix=new Array(9);
  constructor(a=1,b=0,c=0,d=0,e=1,f=0,g=0,h=0,i=1){this.#matrix=[a,b,c,d,e,f,g,h,i]}
  add(M){let[A,B,C,D,E,F,G,H,I]=this.#matrix,[a,b,c,d,e,f,g,h,i]=M.matrix;this.#matrix=[A+a,B+b,C+c,D+d,E+e,F+f,G+g,H+h,I+i];return this}//加算
  sub(M){let[A,B,C,D,E,F,G,H,I]=this.#matrix,[a,b,c,d,e,f,g,h,i]=M.matrix;this.#matrix=[A-a,B-b,C-c,D-d,E-e,F-f,G-g,H-h,I-i];return this}//減算
  scalarMultiple(n=1){let[A,B,C,D,E,F,G,H,I]=this.#matrix;this.#matrix=[A*n,B*n,C*n,D*n,E*n,F*n,G*n,H*n,I*n];return this}//スカラー倍
  multiply (M) {let[A,B,C,D,E,F,G,H,I]=this.#matrix,[a,b,c,d,e,f,g,h,i]=M.matrix;this.#matrix=[A*a+B*d+C*g,A*b+B*e+C*h,A*c+B*f+C*i,D*a+E*d+F*g,D*b+E*e+F*h,D*c+E*f+F*i,G*a+H*d+I*g,G*b+H*e+I*h,G*c+H*f+I*i];return this}//乗算
  get transpose(){let[A,B,C,D,E,F,G,H,I]=this.#matrix;return new this.constructor(A,D,G,B,E,H,C,F,I)}//転置
  get det(){let[A,B,C,D,E,F,G,H,I]=this.#matrix;return A*E*I-A*F*H+B*F*G-B*D*I+C*D*H-C*E*G}//行列式(determinant)
  get inverse(){let[A,B,C,D,E,F,G,H,I]=this.#matrix,e=1/this.det;return new this.constructor((E*I-F*H)*e,-(B*I-C*H)*e,(B*F-C*E)*e,-(D*I-F*G)*e,(A*I-C*G)*e,-(A*F-C*D)*e,(D*H-E*G)*e,-(A*H-B*G)*e,(A*E-B*D)*e)}//逆数
}

JavaScript で「順列」と「組合わせ」を計算する

//組合わせ c= 1:重複あり, 0:重複なし
function combinations (a = [ ], b = a.length, c = 1) {
  return 2 > b
  ? a.map(a =>[a])
  : a.flatMap ((d,e)=> combinations (a.slice (e+1-c), b-1, c).map (e=> [d,...e]));
}


//順列 c= 1:重複あり, 0:重複なし
function permutations (a =[ ], b = a.length, c = 1) {
  return 2 > b
  ? a.map (a => [a])
  : a.flatMap ((d,e,[...f]) => permutations ((f.splice (e,1-c), f), b - 1, c).map(a => [d, ...a]));
}



let ary = [1,2,3,4];
console.log(combinations(ary,3));
console.log(Permutations(ary,3,0));

1行で書く

//組合わせ c= 1:重複あり, 0:重複なし
const permutations=((F=(a=[],b=a.length,c=1)=>2>b?a.map(d=>[d]):a.flatMap((d,e,[...f])=> F((f.splice(e,1-c),f),b-1,c).map(a=>[d,...a])))=>F)();

//順列 c= 1:重複あり, 0:重複なし
const combinations=((f=(a=[],b=a.length,c=1)=>2>b?a.map(d=>[d]):a.flatMap((d,e)=>f(a.slice (e+1-c),b-1,c).map(a=>[d,...a])))=>f)();

「組合わせ」を返す関数

function mathematics (ary = [ ], n = ary.length) {
  if (1 === n) {
    return ary.map (a=> [a]);
  } else {
    return ary.flatMap ((a, i)=> mathematics (ary.slice (i), n -1).map (b=> [a,...b]))
  }
}

console.log (mathematics ([1,2,3],3));
/*
[1,1,1],
[1,1,2],
[1,1,3],
[1,2,2],
[1,2,3],
[1,3,3],
[2,2,2],
[2,2,3],
[3,3,3]
*/

やっぱり1行で済ます

const  mathematics=((f=(a=[],b=a.length)=>b==1?a.map(c=>[c]):a.flatMap((c,d)=>f(a.slice(d),b-1).map(d=>[c,...d])))=>f)();

で目的は

Javascript で、0000 から 9999 までの表を作りたい。 -HTML の <table- JavaScript | 教えて!goo

<!DOCTYPE html>
<title></title>
<meta charset="utf-8">
<style>
  td { text-align: right; width: 6em;}
</style>
<body>
<table border="1" id="T"></table>

<script>
const
  ary2tbody=(a,b=document.createElement('tbody'))=>a.reduce((b,a)=>(a.reduce((c,d)=>(c.insertCell().append(d||''),c),b.insertRow()),b),b),
  transpose=a=>a[0].map((_,i)=>a.map(b=>b[i])),
  mathematics=((f=(a=[],b=a.length)=>b==1?a.map(c=>[c]):a.flatMap((c,d)=>f(a.slice(d),b-1).map(d=>[c,...d])))=>f)(),
  sum=(a,b)=>a+b;


const A = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const B = [[0], ...mathematics (A,1), ...mathematics (A,2), ...mathematics (A,3), ...mathematics (A,4)];
const C = [...Array(10)].map (()=>[]);

B.forEach (a=> C[a.reduce (sum, 0) % 10].push (a ? a.join``.padStart (4,0) : ''));
ary2tbody (transpose(C), T);

</script>
    

canvas のツール 網目がうまく描けない(未完成)

<!DOCTYPE html>
<meta charset="UTF-8">
<title>HTML Canvas Library</title>
<style>
canvas {
  border: 2px gray ridge;
}
</style>
<body>
<canvas width="500" height="250"></canvas>
<canvas width="500" height="250"></canvas>
<canvas width="500" height="250"></canvas>
<canvas width="500" height="250"></canvas>
<canvas width="500" height="250"></canvas>

<script>

class Canvas {
  #col = 'black'; #view = [ ];

  constructor (canvas, option) {
    this.canvas   = canvas;
    this.option   = Object.assign ({ }, this.constructor.defOption, option);
    this.ctx      = canvas.getContext ('2d');
  }

  cls () {
    this.canvas.width = this.canvas.clientWidth;
    this.viewport (...this.#view);
    return this;
  }

  line (x, y, x1, y1) {
    let { ctx } = this;
    ctx.beginPath ();
      ctx.moveTo (x, y);
      ctx.lineTo (x1, y1);
    ctx.closePath ();
    ctx.stroke ();
    return this;
  }

  viewport (maxX = this.canvas.clientWidth, maxY = this.canvas.clientHeight, minX = 0, minY = 0, deg = 0) {
    const
      { canvas, ctx, option } = this,
      { sin, cos, trunc: int, abs, log10, PI: pi } = Math,
      digitNum = n=> int (log10 (n) + 1);//桁数を求める

    let
      w = canvas.clientWidth,
      h = canvas.clientHeight,
      dx = maxX - minX,
      dy = maxY - minY,
      uw = w / dx,
      uh = h / dy,
      ur = deg * (pi / 180);

    ctx.setTransform (
      uw * cos (ur),
      -sin (ur) * uh,
      -sin (ur) * uw ,
      -uh * cos (ur),
      -minX * uw,
      maxY * uh,
    );

    ctx.lineWidth = 1 / (uw + uh) * 1.;//線の太さは、縦横軸の平均から算出

    if (option.grid) {
      ctx.save ();
        //グリッドの間隔を2点の距離から算出する
        let
          mx = 10 ** (digitNum (uw)),
          my = 10 ** (digitNum (uh)),
          nx = mx / 10,
          ny = my / 10,
          sx = int ((minX - abs (dx)) / nx) * nx,
          ex = int ((maxX + abs (dx)) / nx) * nx,
          sy = int ((minY - abs (dy)) / ny) * ny,
          ey = int ((maxY + abs (dy)) / ny) * ny;

console.log(uw, uh,mx,my, sx,ex,sy,ey);

        this.color = 'silver';
//        for (let p = sx; p < ex; p += nx / 100) this.line (p, sy, p, ey);
//        for (let p = sy; p < ey; p += nx / 100) this.line (sx, p, ex, p);

        this.color = 'silver';
//        for (let p = sx; p < ex; p += nx / 10) this.line (p, sy, p, ey);
//        for (let p = sy; p < ey; p += nx / 10) this.line (sx, p, ex, p);

        this.color = 'gray';
        for (let p = sx; p < ex; p += nx) this.line (p, sy, p, ey);
        for (let p = sy; p < ey; p += ny) this.line (sx, p, ex, p);

        this.color = 'black';
        this.line (0, sy, 0, ey);
        this.line (sx, 0, ex, 0);

      ctx.restore ();
    }
    
    this.#view = [maxX, maxY, minX, minY, deg];
    return this;
  }


  set color (arg) {this.ctx.strokeStyle = this.#col = arg; }
  get color () { return this.#col; }


  static defOption = {
    grid: true,
  }

  static create (canvas = document.querySelector ('canvas'), option = { }) {
    return new this (canvas, option);
  }
}


</script>