[Nuxt.js/JavaScript]APIから取ってきたオブジェクトをイジって配列に

もう何回もやっている（はず）なのに全然覚えられないので備忘録。
nuxt.jsで、APIから取ってきた値をVuetifyの[v-select](https://vuetifyjs.com/ja/components/selects/#section-4f7f304465b9)に合うような形にして表示させる…ということをしました。

これを
```json
{
　　"1696": "参加無料",
 "121": "働き方改革",
 "342": "大阪",
 "29": "AI",
 "133": "投資",
 "130": "NY",
 "37": "アート",
 "224": "テクノロジー",
 "54": "イノベーション",
 "246": "スタートアップ",
}
```

こうしたい
```javascript
  {
    id: 1696,
    label: '参加無料'
  },
  {
    id: 121,
    label: '働き方改革'
  },
  {
    id: 342,
    label: '大阪'
  }
  // 以下略
```
オブジェクト内の"fields"内"tag"だけ取り出して、idとlabelというプロパティを持った連想配列にしたいです。vuetifyのセレクトボックスに合う形にしたいからです。
（vuetiyのSelects(v-select)というコンポーネントは、プロパティをlabelに設定しておくと、vuetifyのv-selectというセレクトボックスで勝手にlabelの値がアイテムに適用される）

~~絶対にもっと賢いやり方があると思う（それだけは、わかる）ので、心優しい方がいたら教えてください…~~

2022.3.29追記
私のコードが頭悪そうなのでどうにかできませんかと先輩に泣きついたらもっと良い方法を教えてくださいました。

## 環境
- MacOS　Monterey 12.1
- @nuxt/cli v2.15.8
- vuetify "2.6.3"



2022.3.29追記
実際の実装に関するコードは後に記すとして、ひとまず先輩から直していただいた正解ドンしておきます。

```vuejs:parent.vue
<script>
export default {
  async asyncData({ app }) {
  // get methodはplugin/init.jsで定義
    const searchForm = await app.$get(SEARCH_FORM_API)
    const searchFields = searchForm.fields
    const tagsObj = searchFields.tag
    console.log(tagsObj)
    // expected output　: {　　"1696": "参加無料",　"121": "働き方改革",...}

    const tagsArray = []
    for (const key in tagsObj) {
      if (Object.hasOwnProperty.call(tagsObj, key)) {
        const value = tagsObj[key]
        tagsArray.push({ label: key, value })
      }
    }
    console.log(tagsArray)
    // expected output　: [  { label: '1696', value: '参加無料' },...]
```

ひとまずやりたいこととしてはこれでOK。


## 以下奮闘記録（ポエム）

Vuetifyを使用した実際の画面の実装と、元々自分でごにょごにょしていた上長コードは以下（特に見る必要はないです）。

```vuejs:parent.vue
<item-format :item-title="titleTag">
	<v-col cols="10" md="10" class="mt-2">
		<selectbox
			v-model="tag"
			:items="tags"
			:multiple="true"
			:chips="true"
		/>
	</v-col>
</item-format>
```

```vuejs:components/atoms/Selectbox.vue
<template>
  <div>
    <v-select
      v-model="innerValue"
      :items="items"
      :label="label"
      :item-text="itemText"
      :item-value="itemValue"
      :multiple="multiple"
      :chips="chips"
    ></v-select>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: [String, Array],
      default: '',
    },
    items: {
      type: Array,
      default: () => [],
    },
    label: {
      type: String,
      default: '選択してください',
    },
    itemText: {
      type: String,
      default: 'label',
    },
    itemValue: {
      type: String,
      default: 'value',
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    chips: {
      type: Boolean,
      default: false,
    },
  },
  computed: {
    innerValue: {
      get() {
        return this.value
      },
      set(val) {
        this.$emit('input', val)
      },
    },
  },
}
</script>

```

このvuetifyのv-select、コンポーネントでアイテムとして指定する値はitems(`:items="tags"`)なんですけど、配列内のオブジェクトのプロパティはlabel(`{ id: 1696, label: '参加無料'},`)が見られるというトリッキーな仕様ですよ…これに関してはずっとこの方法でやっているのですが[公式](https://vuetifyjs.com/en/api/v-select/#props)の該当箇所が見つからず、ご存知の方がいたら教えてください。

## まずはasyncでAPI取得
SEARCH_FORM_APIで返ってくる値が確認できたので、返ってくる値をfields以下に絞ります。
```vuejs:parents.vue
<script>
// APIの取得先は~/constants/urlsに格納
import { SEARCH_FORM_API } from '~/constants/urls'
export default {
  async asyncData({ app }) {
    // plugins/init.jsでgetメソッドを定義してます
    const searchForm = await app.$get(SEARCH_FORM_API)
    const searchFields = searchForm.fields
</script>
```
そそてさらにそれをtag以下に絞ります
```vuejs:parents.vue
<script>
    const tagsObj = searchFields.tag
</script>
```
tagsObjの中身は今こんな感じ。形は何も加工していません。
```
    {
      "1696": "参加無料",
      "121": "働き方改革",
      "342": "大阪",
      "29": "AI",
      "133": "投資",
      "130": "NY",
      "37": "アート",
      "224": "テクノロジー",
      "54": "イノベーション",
      "246": "スタートアップ",
    }
```
繰り返しになりますがこれをこうしたい。
```javascript
  {
    id: 1696,
    label: '参加無料'
  },
  {
    id: 121,
    label: '働き方改革'
  },
  {
    id: 342,
    label: '大阪'
  }
  // 以下略
```

自分なりにごにょごにょして、まずはAPIで取ってきたtagオブジェクトを配列に変換することにしました。
```vuejs:parents.vue
<script>
    const tagsArray = Object.keys(tagsObj).map((x) => [Number(x), tagsObj[x]])
</script>
```
この時点でのtagsArrayの中身
```
[
  [ 1696, '参加無料' ],
  [ 121, '働き方改革' ],
  [ 342, '大阪' ],
]
```
あと一息なのでは！？

もっと短くできそうな気もしますが、tagsという配列を新しく作り、その中にオブジェクトをあてがうことにしました。for文でassign()を配列の１つ１つの要素に適用します。
参考： [JavaScript で配列をオブジェクトに変換する](https://www.delftstack.com/ja/howto/javascript/array-to-objects-javascript/)


```vuejs:parents.vue
<script>
    // 配列の中の配列をオブジェクトに
    const tags = []
    for (let i = 0; i < tagsArray.length; i++) {
      tags.push(Object.assign({}, tagsArray[i]))
    }
</script>
```

この時点でのtagsの中身はこうです。うおおおお
assign()のプロパティは順番に、0,1,2...と付けられていくんですね。
```
[
  { '0':169, '1':'参加無料' },
  { '0':121, '1':'働き方改革' },
  { '0':342, '1':'大阪' },
]
```
あとはプロパティの名前を変えるだけ。for文の中に追記します。
参考：
- [オブジェクトのプロパティ名を変更する方法！Javascriptの勉強](https://programmer-life.work/javascript/object-rename-key-javascript)
- [JavaScript: Object Rename Key](https://stackoverflow.com/questions/4647817/javascript-object-rename-key)

deleteの箇所、参考サイトには1行で書く方法なども記載されています。
頭悪そうなコードになってしまいますが、ひとまずはゴール優先で…

```vuejs:parents.vue
<script>
    const tags = []
    for (let i = 0; i < tagsArray.length; i++) {
      tags.push(Object.assign({}, tagsArray[i]))
      // プロパティ名を変更
      tags[i].id = tags[i][0]
      tags[i].label = tags[i][1]
      delete tags[i][0]
      delete tags[i][1]
    }
    console.log(tags) //　{　id: 1696, label: '参加無料'},の形
</script>
```

async内は最終的にこうなりました。
```vuejs:parents.vue
<script>
export default {
  async asyncData({ app }) {
    const searchForm = await app.$get(SEARCH_FORM_API)
    const searchFields = searchForm.fields
    // console.log(searchFields)

    const departments = searchFields.department_code
    // console.log(departments)

    const departmentsLabel = Object.values(departments)
    // console.log(departmentsLabel)

    const onlineStat = searchFields.online

    // オブジェクトの順序が変わってしまうので要相談
    // const result = Object.keys(obj).map((key) => [Number(key), obj[key]])
    const tagsObj = searchFields.tag

    // APIで取ってきたtagオブジェクトを配列に変換
    const tagsArray = Object.keys(tagsObj).map((x) => [Number(x), tagsObj[x]])
    // console.log(tagsArray)

    // 配列の中の配列をオブジェクトに
    const tags = []
    for (let i = 0; i < tagsArray.length; i++) {
      tags.push(Object.assign({}, tagsArray[i]))
      // プロパティ名を変更
      tags[i].id = tags[i][0]
      tags[i].label = tags[i][1]
      delete tags[i][0]
      delete tags[i][1]
    }
    console.log(tags) // {　id: 1696, label: '参加無料'},の形

    return { tags }
  },
}
</script>
```

こんな見た目になっています。
![スクリーンショット 2022-03-25 16.53.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97385/831ed109-3718-e78e-6b82-99f90d0cbc4e.png)

## おしまい
配列とオブジェクトのコンバートって、初学者に取っては鬼門ですね。慣れるまではしょうがないと思って、粛々と書いていこうと思います。
そしてお詳しい方に伺いたいのですが、APIから取ってきたオブジェクトの順番をそのままにしてjavascriptで表示するにはどうすれば良いでしょうか？例えば、APIはタグ利用頻度の上位100件が返ってきているのに、加工すると順番が崩れるという...OMG