質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.21%

連想配列の使い方、要素の参照方法が判らないので教えて下さい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 153

hatsuzo

score 27

連想配列の使い方、要素の参照方法が良く判らないので教えて下さい。

jQueryのjqGridで作成されている配列オブジェクトの値をCSVに出力するスクリプトです。
先人のソースを参考にして見よう見まねで、何とか動くようになり、結果をExcelで開くことも出来るのですが、
いわゆる標準的なCSVの形式として、文字列をダブルクォーテーションで囲む、という形式にすることが出来ていません。
列の属性を判断するには、sorttypeというプロパティを持っているのでそれを参照して、文字か数字かを判断すればよいと思っています。

連想配列の並び順が確保できなかったので、列の順番を付けるためにkeyOrderという配列を設けています。
この配列に値の順に、colModelの中のsorttypeプロパティが'int'の場合には数値と見なすという判断ができればよいかと思うのですが、
どのように検索、参照すればよいのでしょうか?

もっと簡単な方法でできるというご意見もあろうことは承知しておりますが、当面の課題におきましてアドバイスをいただければ幸いです。

よろしくご指導ください。

var dt = [
   {cd:"A001", name:"鈴木", age:35}
  ,{cd:"A002", name:"佐藤", age:45}
  ,{cd:"A003", name:"田中", age:55}
];

var keyOrder  = [
    "cd",
    "name",
    "age"
];


$("#list").jqGrid({
   data: dt
  ,datatype: "local"
  ,colNames:['CD', '名前', '年齢']
  ,colModel:[
    {index:'cd', name:'cd', width:'50px', align:'center'},
    {index:'name', name:'name', width:'100px', align:'left'},
    {index:'age', name:'age', width:'80px', align:'right', sorttype:'int'},
  ]
  ,height: 150
  ,multiselect: true
  ,caption: '一覧'
  ,rowNum: 5
  ,rowList: [5, 10, 20]
  ,viewrecords: true
  ,pager: 'pg01'
});

var ListToCSV = {
    export: function() {
        var csv_data = "";
        var j;
        var line;
        // 行データを取得する
        var sel_list = $('#list').getGridParam('data');
        j = 0;
        line = "";
        var header_list = $('#list').getGridParam('colNames');
        // 列属性の取得  --> ここから sorttypeプロパティを取り出したい
        var col_list = $('#list').getGridParam('colModel');
        // 見出しの値を取得
        header_list.forEach(function( key ) {
            // chekbox列の除外
            if ( key.indexOf('checkbox') == -1) {
                if ( j != 0 ) line += ",";
                line += key;
                j ++;
            }
        });
        line += '\n';
        csv_data += line;
        // 明細の値を取得
        for (var i = 0; i < sel_list.length; i++) {
            j = 0;
            line = "";
            // 連想配列は並び順が保証されないので定義された順番(keyOrder)に書き出す
            keyOrder.forEach(function( key ) {

                //
                // この部分で、keyの値に対応する列のsorttypeというプロパティを参照
                //
                // col_list[key]['sorttype'] では駄目でした。

                if(sel_list[i][key]){
                    var innerValue = sel_list[i][key]===null?'':sel_list[i][key].toString();
                } else {
                    var innerValue = '';
                }
                // " を "" に置換
                var result = innerValue.replace(/"/g, '""');
                // , 又は \n を含んでいる場合は ""で囲う
                if (result.search(/("|,|\n)/g) >= 0)
                    result = '"' + result + '"';
                if ( j != 0 ) line += ",";
                line += result;
                j ++;
            //}
            });
            console.log(">" + line);
            line += '\n';
            csv_data += line;
        }
        // 頭にbomコードを付けた
        var csv_data_sjis = "\uFEFF" + csv_data;
        return csv_data_sjis;
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • think49

    2021/02/23 21:11

    HTMLを含めた再現コードを書いてください。
    もしくは、必要なデータをコピペ可能な形式で書いてください。
    こちらで再現できなければ、正確な回答は出来ません。

    > 列の属性を判断するには、sorttypeというプロパティを持っているのでそれを参照して、文字か数字かを判断すればよいと思っています。

    この部分が理解できません。

    キャンセル

  • hatsuzo

    2021/02/24 09:07

    think49さん コメントありがとうございました。

    すみません。説明が足りませんでした。
    $("#list").jqGrid({
    data: dt
    ,datatype: "local"
    ,colNames:['CD', '名前', '年齢']
    ,colModel:[
    {index:'cd', name:'cd', width:'50px', align:'center'},
    {index:'name', name:'name', width:'100px', align:'left'},
    {index:'age', name:'age', width:'80px', align:'right', sorttype:'int'},
    ]
    (以下略)
    }
    dataプロパティはデータの実体となる配列で、colModelプロパティで各列に対する属性が配列で定義されています。
    その中の属性の一つとして、sorttypeがあり、その値がintであれば数値属性であることが識別できる
    ということです。
    データの実体から見ると、
    {cd:"A001", name:"鈴木", age:35}
    age列が数値属性であるという意味になります。
    実際、フレームワーク(intra-mart)を使っているので、再現コードが書けないため、
    $("#list").jqGrid({ の部分やデータの実体部分は実ソースには無いものですが、参照可能な
    データの形式で追記したものです。

    キャンセル

  • think49

    2021/02/24 12:37 編集

    説明を努力しているのは伝わりましたが、まだ正確な状況がわかりません。

    コピペで動作して、私が「期待するCSV文字列値」を返す関数に修正して頂ければ、修正可能と思います。
    簡単にいえば、「入力値のサンプルコード」と「出力値のサンプル文字列」があれば、他は不要です。

    キャンセル

回答 2

check解決した方法

0

お騒がせしました。
オブジェクトを持つ配列のインデックスを行、プロパティを列とみなした場合、
特定の列の値がキーワードに合致している行を探し、その行の別のプロパティを参照する、
つまりExcel関数でいうVlookupをする方法が判ればよいことに気づきました。
毎度colModelという配列にアクセスしているので不細工ですが、とりあえず動作しましたので、
ご連絡します。
アドバイスを頂きまして有難うございました。

$("#list").jqGrid({
   data: dt
  ,datatype: "local"
  ,colNames:['CD', '名前', '年齢']
  ,colModel:[
    {index:'cd', name:'cd', width:'50px', align:'center'},
    {index:'name', name:'name', width:'100px', align:'left'},
    {index:'age', name:'age', width:'80px', align:'right', sorttype:'int'},
  ]
  ,height: 150
  ,multiselect: true
  ,caption: '一覧'
  ,rowNum: 5
  ,rowList: [5, 10, 20]
  ,viewrecords: true
  ,pager: 'pg01'
});

var ListToCSV = {
    export: function() {
        var csv_data = "";
        var j;
        var line;
        // 行データを取得する
        var sel_list = $('#list').getGridParam('data');
        j = 0;
        line = "";
        var header_list = $('#list').getGridParam('colNames');
        var col_list = $('#list').getGridParam('colModel');    // ##### この行追加
    for (var i = 0; i < sel_list.length; i++) {
        j = 0;
        line = "";
        keyOrder.forEach(function( key ) {
            // ##### ここから追加
            const col = col_list.find((p) => {
                return (p.name === key);
            });
            let intatr = false;
            if (col.sorttype){
                if (col.sorttype === "int"){
                    intatr = true;
                }
            }
            // ##### ここまで追加
            if(sel_list[i][key]){
                var innerValue = sel_list[i][key]===null?'':sel_list[i][key].toString();
            } else {
                var innerValue = '';
            }
            // " を "" に置換
            var result = innerValue.replace(/"/g, '""');
            // , 又は \n を含んでいる場合は ""で囲う
            //if (result.search(/("|,|\n)/g) >= 0)
            if (result.search(/("|,|\n)/g) >= 0 || intatr == false)    // ##### この行修正
                result = '"' + result + '"';
                if ( j != 0 ) line += ",";
                line += result;
                j ++;
            });
            console.log(">" + line);
            line += '\n';
            csv_data += line;
        }
            // 頭にbomコードを付けた
            var csv_data_sjis = "\uFEFF" + csv_data;
            return csv_data_sjis;
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

CSV

コードを見たところ、下記のように扱うようなので、

 変数名   CSV上の扱い 
 keyOrder   列名 
 dt   Object.values() がセル値 

その前提でコードを書きました。

<pre></pre>
<script>
'use strict';
function toCsv (keys, body) {
  const header =  keys.map(key => '"' + key.replace(/"/g, '""') + '"').join();

  body = body.map(function (obj) {
    return this.map(key => '"' + String(obj[key]).replace(/"/g, '""') + '"').join(); 
  }, keys).join('\r\n');

  return header + '\r\n' + body;
}


const dt = [
  {cd:'A001', name:'鈴木', age:35},
  {cd:'A002', name:'佐藤', age:45},
  {cd:'A003', name:'"田中\n太郎"', age:55}
];

const keyOrder  = ['cd','name','age'];

document.querySelector('pre').append(toCsv(keyOrder, dt));
</script>

RFC4180準拠。

new Map

連想配列の並び順が確保できなかったので、列の順番を付けるためにkeyOrderという配列を設けています。

順番が重要なら連想配列(という名の new Object)を使用するべきではありません。
new Map を使用しましょう。

Re: hatsuzo さん

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/02/24 09:49 編集

    think49さん 貴重なお時間を頂き、アドバイス有難うございました。

    配列からCSVへの変換、シンプルに書けるのですね。
    また、CSVの形式について、””は必ずしも必須ではないということ判りました。

    昔から数字を文字属性として扱ってExcelで開かせたいような場合は、敢えて””で囲うべきと思っていたのですが、実際にExcelでそのように加工したCSVを開いても、敢えてテキスト形式として列ごとに属性指定しない限りは、結局は数字になってしまうので、あまり意味が無い、とは思っていました。
    しかし、受け取るアプリによっては、必ずしもそうではない可能性もあり、また、区切り文字がフィールド内に入ってしまう可能性もあるので、文字列を””で囲うのは意味の無いことではないと思っています。

    また、mapを使ったことのない私の理解不足かもしれませんが、見出しの文字列がデータ配列の中ではなく、前述したプロパティの中に定義されているため、せっかくですが、今回の事例ではこの方法では難しいと思います。

    キャンセル

  • 2021/02/24 12:40 編集

    CSVは単純なテキストデータで、セルに書式の概念はありません。
    MS ExcelはCSVインポートする際に書式を指定出来ますが、他アプリでどのように扱うかはアプリ依存になります。
    セル値を"で括るのは、カンマ/改行/ダブルコーテーション等のメタキャラクタを含む事が出来る以上の効果はありません。

    > 見出しの文字列がデータ配列の中ではなく、前述したプロパティの中に定義されているため、

    プロパティが定義されたオブジェクトデータのサンプルコードがあれば、対応出来るかもしれません。

    キャンセル

  • 2021/02/24 15:03

    有難うございました。
    文言の意味がよく判っていないので、うまく説明ができていないみたいです。
    jqGridとかCSVとかの本題ではないキーワードが登場してしまったので、判りにくくしていました。
    要は、オブジェクトを持つ配列のインデックスを行、プロパティを列とみなした場合、
    特定の列の値がキーワードに合致している行を探し、その行の別のプロパティを参照する、
    つまりExcel関数でいうVlookupをする方法が判ればよいことに気づきました。
    vlookup(キーの値、行列の範囲、返す列)
    データ実体の行ごとに、何度もプロパティを参照しなくてはいけないので、
    あまりスマートな方法ではないですが、なんとか参照はできそうです。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る