GAS × LastPassAPI スプレッドシートにLastPassに格納されている情報を書き込みたい その6 複数階層のオブジェクトから値を取り出す for...in/forEach (完)
前回
データの構造が見えてきたので、うまいことやって繰り返し処理をして全データを取得していくです。
全体像コード例を先に示します。
説明のためにコメント多めです。
全体的な流れとしては下記の通りです。
レスポンスを得る
整える
スプレッドシートに書き出す
Github
1.レスポンスデータ
1-1.レスポンスを得る
function getSharedFolderDataViaLastPassAPI() {
const url = 'https://lastpass.com/enterpriseapi.php';
const scriptProperties = PropertiesService.getScriptProperties();
const cid = scriptProperties.getProperty('AccountNumber');
const provhash = scriptProperties.getProperty('ProvisioningHash');
const options = {
"method": "POST",
"header": { "Content-Type": "application/json", },
"payload": {
"cid": cid,
"provhash": provhash,
"cmd": "getsfdata",//getdetailedsfdata だと 429エラー。 "getuserdata", "getsfdata"は通る
"data": "all"
}
}
const response = UrlFetchApp.fetch(url, options);
console.log(`response: ${response}`);
const obj = JSON.parse(response);
console.log(obj);
return obj;
}
1-2. サンプル、テスト用データ
const response = {
"12345678": {
"sharedfoldername": "hoge",
"deleted": false,
"score": 50,
"users": [
{
"username": "hoge@hoge.com",
"readonly": "0",
"give": "1",
"can_administer": "1",
"superadmin": false,
"deletedstatus": "0"
},
{
"username": "fuga@hoge.com",
"readonly": "0",
"give": "1",
"can_administer": "1",
"superadmin": false,
"deletedstatus": "0"
},
{
"username": "hege@hoge.com",
"readonly": "1",
"give": "0",
"can_administer": "1",
"superadmin": false,
"deletedstatus": "0"
}
]
},
"87654321": {
"sharedfoldername": "hogu",
"deleted": false,
"score": 50,
"users": [
{
"username": "hogu@hogu.com",
"readonly": "0",
"give": "1",
"can_administer": "1",
"superadmin": false,
"deletedstatus": "0"
}
]
}
}
2.レスポンスデータを整える
for...in/forEachどちらでもいいです。やり方は色々あるよってことで。
どちらにしても、入れ子構造でデータを取り出しています。
2-1. for...inの例
function forin() {
const response = getSharedFolderDataObjectViaLastPassAPI();
const values = []; // 空の配列を用意
for (const folderId in response) { // Key=folderId responseの中にあるfolderId分、反復処理する
// console.log(folderId); // ex: 12345678
const users = response[folderId]['users']; // 各オブジェクトの users プロパティ取り出しておく
// console.log(users);
for (const user of users) {
// console.log(user);
const record = Object.values(response[folderId]); // [ 'hoge', false, 50, [[Object],[Object],[Object],[Object]] ]
// console.log(record);
const userInfo = Object.values(user); // [ 'hoge@hoge.com', '0', '1', '1', false, '0' ]
// console.log(userInfo);
record[3] = userInfo; //record[3]を userInfoに置き換える[ 'hoge', false, 50, [ 'hoge@hoge.com', '0', '1', '1', false, '0' ]],
record.unshift(folderId); //配列の先頭にfolderIdを入れる
values.push(record.flat());//配列にrecordを入れて、flatで一次元配列にする
}
}
console.log(values);
return values;
}
2-2. forEachの例
function forEach() {
const response = getSharedFolderDataObjectViaLastPassAPI();
const sharedFolderIds = Object.keys(response);// オブジェクトの第1階層のKeyを全て取得。例 12345678
// console.log(sharedFolderIds);
const values = [];// 空の配列を用意
sharedFolderIds.forEach(shareFolderId => { // forEachで各shareFolderIdに対して処理
const object = response[shareFolderId]; // オブジェクトの第2階層=各shareFolderIdの中身
// console.log(object);
const { sharedfoldername, deleted, score, } = object; // 分割代入
// console.log(sharedfoldername);
const record = [shareFolderId, sharedfoldername, deleted, score];// 配列として値を格納
// console.log(record);
object.users.forEach(user => { // forEachで各user対して処理
const { username, readonly, give, can_administer, superadmin } = user; // 分割代入
const tmpRecord = [...record];// スプレッド構文。recordの要素をtmpRecordとして配列の中に展開。
// console.log(tmpRecord);
tmpRecord.push(username, readonly, give, can_administer, superadmin);
values.push(tmpRecord);
});
});
console.log(values);
return values;
}
for...in/forEach どちらでも得られる結果は同じです。
どちらもキーを主軸に、その数の分だけ処理をぶん回しているイメージです。
最終的にスプレッドシートに書き出すために、どちらも配列に格納しています。
forEachで、recordに一旦格納して、それを次のforEachでまた展開して一行のレコードにしていくというがアツい。
特にこの辺りは、@etau @Okapie @NAOP4P4 @mkataoka73 などに壁打ちに付き合っていただきながら、あーだこうだ言い合って楽しかったです〜〜!!!感謝!!!
3.スプレッドシートに書き出す
function setValuesOnSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('シート1');
const values = forEach();
console.log(values);
console.log(values[0]);
sheet.getRange(2, 1, values.length, values[0].length).clear();
SpreadsheetApp.flush();
sheet.getRange(2, 1, values.length, values[0].length).setValues(values);
}
一行目は見出しとして項目名称を入れるだろうという想定。
内容をクリアして、SpreadsheetApp.flush(); で効かせてからset
こんな感じです。
以下は単なる個人的なめも
この先は個人的に気になったところのめもです。
要素数の揺れがある場合
そんな構造を返してくるとは考えにくい、無いと信じたい。
が、もしあったらどうする?うわ〜〜めんどくせ〜〜〜〜
で、これに向き合った猛者がいるんですね。
@Okapie さん、アウトプットお待ちしております❤️
/* responsesの中身だけ以下の4つのモデルケースに
置き換えて出力してみました。userの数は各フォルダ2名に簡略化しています */
const responses = {
11111111: {// ケース①:追加プロパティなし
sharedfoldername: '[Delete]fuga_1',
deleted: true,
score: 11.1,
users: [ //userはモデルケースでは各フォルダ2人だけにしています
{
"username": "fuga_1_user1_追加なしの場合@lastpass.com",
"readonly": 0,
"give": 1,
"can_administer": 1
},
{
"username": "fuga_1_user2_追加なしの場合@lastpass.com",
"readonly": 1,
"give": 0,
"can_administer": 0
}
]
},
22222222: { // ケース②:userの中に追加プロパティがある場合
sharedfoldername: 'fuga_2',
deleted: false,
score: 22.2,
users: [
{
"username": "fuga_2_user1_user内で追加の場合@lastpass.com",
"readonly": 0,
"give": 1,
"can_administer": 1,
"additionalProp1": false,
"additionalProp2": 0,
"additionalProp3": "fuga admin",
},
{
"username": "fuga_2_user2_user内で追加の場合@lastpass.com",
"readonly": 1,
"give": 0,
"can_administer": 0
}
]
},
33333333: { // ケース③:usersの後に追加プロパティがある場合
sharedfoldername: 'fuga_3',
deleted: false,
score: 33.3,
users: [
{
"username": "fuga_3_user1_usersの後に追加の場合@lastpass.com",
"readonly": 0,
"give": 1,
"can_administer": 1
},
{
"username": "fuga_3_user2_usersの後に追加の場合@lastpass.com",
"readonly": 0,
"give": 1,
"can_administer": 1
}
],
additionalProp4: false,
additionalProp5: 0,
additionalProp6: "fuga admin",
},
44444444: { // ケース④:usersの前に追加プロパティがある場合(ズレが生じる)
sharedfoldername: 'fuga_4',
deleted: false,
score: 44.4,
additionalProp7: false,
additionalProp8: 0,
additionalProp9: "fuga admin",
users: [
{
"username": "fuga_4_user1_usersの前に追加の場合@lastpass.com",
"readonly": 0,
"give": 1,
"can_administer": 1
},
{
"username": "fuga_4_user2_usersの前に追加の場合@lastpass.com",
"readonly": 0,
"give": 1,
"can_administer": 1
}
]
},
};
}
あれ、いきなり配列に突っ込んだらあかんかったっけ
いきなりrecordにpushすると、最後は空配列になる。
ここが上手く説明できないので、デバッグしてみます。
あ、違うわ、これ。values.pushにしないとだ。寝ぼけてんな。
values.pushでこうなるじゃん。
そらそうだよな、こうするとrecordを活かしてないからこうなるわな。
うん、自己解決したな。
は〜〜〜〜、オブジェクト、配列、理解が深まったかな。
#GAS
#API
#パスワード管理
#json
#httpリクエスト
#LastPass
#配列
#オブジェクト
いただいたサポートで、書籍代や勉強費用にしたり、美味しいもの食べたりします!