ユーザ選択で選択した複数のユーザの優先組織を組織選択に表示したい

お世話になっております。ソースコードを含むため長文ですが、よろしくお願いいたします。
標題の通り、現在、

ユーザー選択で選択したユーザーの優先組織を組織選択に表示したい

を参考に、ユーザ選択で選択した複数のユーザの優先組織を組織選択に表示したいと考えております。
(グラフのクロス集計で組織毎での件数を表示したいため)
前提:【組織とユーザの所属状況】
組織A:xさん、yさん
組織B:zさん

後述のソースコードで
ユーザ選択 組織選択
xさん 組織A
zさん 組織B

のように表示することにはほぼ成功しました。
しかし、下記の2つの問題を抱えております。

1)組織A、組織Bの順番に表示したいのに組織B、組織Aの順番で表示されることがある
→恐らくfor inの構文を使っているせいだと思うのですが、どうすれば確実に組織A、組織Bの順番に表示できますでしょうか。

2)同じ組織に所属している人をユーザ選択で選択した場合、コンソールに「TypeError: resp2.organizations[k] is undefined」というエラーが出てしまう
→1)と同じfor inの構文の辺りに問題があることはわかるのですが、どうすれば解消されるでしょうか。
【期待する挙動】
ユーザ選択 組織選択
xさん 組織A
yさん 組織B
zさん

素人なもので、javascriptもkintoneのAPIも理解していないところがあります。
大変申し訳ありませんが、よろしくお願いいたします。

現在のソースコード

(function() {
  "use strict";
  kintone.events.on(['app.record.create.change.ユーザ選択','app.record.edit.change.ユーザ選択'], function(event) {
    var userId =[];
    for (var i in event['record']['ユーザ選択'].value){
      userId.push(event['record']['ユーザ選択']['value'][i]['code']); //ユーザコード取得
    }
  var body = {
   "codes": userId
  };
  // ユーザcodeをkeyにしてユーザの詳細を取得
  kintone.api(kintone.api.url('/v1/users', true), 'GET', body, function(resp) {
    var orgId =[];
    for (var j in event['record']['ユーザ選択'].value){
       orgId.push(resp.users[j].primaryOrganization); // 優先組織のIDを取得
    }
    var body2 = {
      "ids": orgId
    };
    // 組織idをkeyにして組織の詳細を取得
    kintone.api(kintone.api.url('/v1/organizations', true), 'GET', body2, function(resp2) {
      var orgCode =[]; // 優先組織のcodeを取得
      for (var k in event['record']['ユーザ選択'].value){
        orgCode.push(resp2.organizations[k].code);
      }
      var orgName =[]; // 優先組織のnameを取得
      for (var l in event['record']['ユーザ選択'].value){
        orgName.push(resp2.organizations[l].name);
      }
      var record = kintone.app.record.get();
      var body3 = [];
      for(var m = 0; m < orgCode.length; m++) {
        body3[m] = {
          "code" : orgCode[m],
          "name" : orgName[m]
        }
      }
      record.record['組織選択'].value = body3;
      kintone.app.record.set(record);
    });
  });
});
})();

 

パッと見ですが、これを実現するための全体的なポイントとしては、APIの挙動・使い方と、重複項目・ソート処理の2点だと思います。

 

問題を2点あげてありますが、共通してあげてあるfor in の点については、for文の使い方に少し問題があるかなぁと思います。

連想配列でもない通常の配列に対して、

for (var k in event['record']['ユーザ選択'].value){
orgCode.push(resp2.organizations[k].code);
}

とするのであれば、

for (var k = 0; k < event['record']['ユーザ選択'].value.length; k++){
orgCode.push(resp2.organizations[k].code);
}

と、よりベーシックなfor文にする方が間違いは減りそうです。

このケースでは基本的には同じ動きをしてくれますが、2) であげてあるエラーの原因にも気付きやすくなるかと思います。2)の直接的な原因は、for文の条件に用いている event.record.ユーザ選択.valueの長さと ブロック内でpushの対象になっている resp.organizationsの長さ が異なることが原因だと思います。そもそも、for文の条件とpushを掛けようとしてる配列が異なるものなので、条件とブロック内の動作で用いる配列は長さが絶対的に同じものであることが担保されてない限りは、ブロック内で用いる配列のインデックスが適切に指定できるようにfor文の条件を設定するのが良いでしょう。

例えばこのように、

for (var k =0; k < resp2.organizations.length; k++){
orgCode.push(resp2.organizations[k].code);
}

pushしたいorganization[].codeの長さに応じて、ループ出来るよう条件を設定する方がベターです。

このように、配列の長さを意識する、期待するブロック内の動作とfor文の条件がマッチできているかという点で、ベーシックな書き方は有用だと思います。

本件では、同じ組織に所属しているユーザーがいる場合と言われていますが、ユーザー2人で所属組織は1つとなったりして、ユーザー数と組織数の配列の長さにアンバランスがあり、これらを処理と条件に使っていたため、処理対象の配列にないインデックスを条件で与えてしまったことが、2)の本質的原因です。

 

また、/users や /organizations のAPIの挙動を把握しておく必要がありそうです。idsもしくはcodesを指定しても、いずれもレスポンスの並びがidの昇順のようです(恐らく、idsやcodesやレスポンスのソート条件までを指定するものではなく、絞り込み条件でしかないものと思われます)。

例えば、

マーケティング組織 id: 1, code: marketing

営業組織 id: 2, code: sales

といった組織が設定されているとして、

codes: [‘sales’, ‘marketing’] と指定しても codes: [‘marketing’, ‘sales’] レスポンスは、idの昇順で次のような並びで得られることになります。

{
“organizations” : [ {
“id” : “1”,
“code” : “marketing”,
“name” : “Marketing”,
“localName” : “”,
“localNameLocale” : “en”,
“parentCode” : null,
“description” : “”
}, {
“id” : “2”,
“code” : “sales”,
“name” : “Sales”,
“localName” : “”,
“localNameLocale” : “en”,
“parentCode” : null,
“description” : “”
} ]
}

従いまして、どうしても、ユーザーの入力順に合わせて組織をセットしていきたければ、一連のプロセスの中でこれを意識しながら、ソートしたり重複する組織を落したりといった処理を加えていく必要がでてきます。kintone.getLonginUser().codeとprimaryOrganizationの紐付けは個別にAPIをコールするしかないと思いますし、これに伴って少なからず処理を書き換えないといけませんし、APIのコール数も増えるので、要件と難易度等を考慮して対応されるのが良いかと思います。

Ryu Yamashita さま

ご回答いただき、ありがとうございます。
問題2)について、例としてご提示いただいた

for (var k =0; k < resp2.organizations.length; k++){
orgCode.push(resp2.organizations[k].code);
}

で作成したところ、同じ組織のユーザ選択をしたときに、
組織を期待する挙動どおりに設定することができました。
ただ、この場合、一旦ユーザ選択をした後に、選択した全てのユーザを削除すると
現在設定している全ての組織が表示されてしまうという現象が発生しました。
【現象】
ユーザ選択 組織選択
組織A
組織B
組織C

そのため、優先組織のIDを取得した後、その配列から重複を除去して
重複除去後の配列の長さを求め、その長さの数だけループを回すという動作に
変更し、下記のソースコードで無事に想定していた挙動を確認しました。

なお、問題1)については、優先組織を自動入力するという今回の仕様上、
入力者が直接、組織を選択する必要性や組織の表示を認識する必要性はないため、
難易度を考慮し、組織選択のフィールドを隠すことにいたしました。

おかげさまで、想定していた挙動を実装することが出来ました。
また、何かありましたらよろしくお願いいたします。

 

<変更したソースコード>

(function() {
  "use strict";
  kintone.events.on(['app.record.detail.show','app.record.create.show','app.record.edit.show'], function(event) {
    kintone.app.record.setFieldShown('組織選択', false); //組織選択のフィールドを隠す
  });
  kintone.events.on(['app.record.create.change.ユーザ選択','app.record.edit.change.ユーザ選択'], function(event) {
    var userId =[];
    for (var i = 0; i < event['record']['ユーザ選択'].value.length; i++){
      userId.push(event['record']['ユーザ選択']['value'][i]['code']); //ユーザコード取得
    }
  var body = {
   "codes": userId
  };
  // ユーザcodeをkeyにしてユーザの詳細を取得
  kintone.api(kintone.api.url('/v1/users', true), 'GET', body, function(resp) {
    var orgId =[];
    for (var j = 0; j < event['record']['ユーザ選択'].value.length; j++){
       orgId.push(resp.users[j].primaryOrganization); // 優先組織のIDを取得
    }
    var orgId2 = orgId.filter((x, y, self) => self.indexOf(x) === y); //優先組織のIDの重複を除去
    var body2 = {
      "ids": orgId2
    };
    // 組織idをkeyにして組織の詳細を取得
    kintone.api(kintone.api.url('/v1/organizations', true), 'GET', body2, function(resp2) {
      var orgCode =[]; // 優先組織のcodeを取得
      for (var k = 0; k < orgId2.length; k++){
        orgCode.push(resp2.organizations[k].code);
      }
      var orgName =[]; // 優先組織のnameを取得
      for (var l = 0; l < orgId2.length; l++){
        orgName.push(resp2.organizations[l].name);
      }
      var record = kintone.app.record.get();
      var body3 = [];
      for(var m = 0; m < orgCode.length; m++) {
        body3[m] = {
          "code" : orgCode[m],
          "name" : orgName[m]
        }
      }
      record.record['組織選択'].value = body3;
      kintone.app.record.set(record);
    });
  });
});
})();