Menu
Coddy logo textTech

Mapの値としてのポインタ

CoddyのGOジャーニー「論理とフロー」セクションの一部 — レッスン 27/68。

構造体をマップに直接格納することは便利ですが、構造体へのポインタを格納することで、強力な機能が解放されます。それは、マップを通じて元の構造体データを直接変更できるという機能です。このアプローチは、データを格納した後に更新する必要がある場合に不可欠です。

構造体へのポインタをマップの値として格納する場合、構造体のコピーではなく、その構造体のメモリ番地を格納することになります。このようなマップの宣言と操作方法は以下の通りです:

type Product struct {
    Price    float64
    Quantity int
}

products := map[string]*Product{
    "laptop": &Product{Price: 999.99, Quantity: 5},
    "mouse":  &Product{Price: 25.50, Quantity: 20},
}

データを変更する必要があるときに、主な利点が明らかになります。ポインタを扱っているため、マップを通じて行われた変更は元の構造体に影響を与えます:

// マップを通じてラップトップの価格を直接更新します
products["laptop"].Price = 899.99
fmt.Println(products["laptop"].Price) // 出力: 899.99

このパターンは、在庫システムやユーザープロファイル、あるいはマップが主要なデータストアとして機能し、変更を永続化させる必要があるシナリオなど、保存されたデータを頻繁に更新する必要があるアプリケーションにおいて特に価値があります。

quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

challenge icon

チャレンジ

簡単

構造体へのポインタをマップの値として使用する利点を示す、シンプルな在庫管理システムを構築してください。このチャレンジでは、ポインタベースのマップによって格納されたデータを直接変更できるようになり、在庫システムを効率的かつ更新に対してレスポンス良くする方法を学びます。

3つの入力を受け取ります:

  • 製品の数を表す文字列(例:"4"
  • 次の形式の製品データを含む文字列:"name1:price1:quantity1,name2:price2:quantity2,name3:price3:quantity3"(例:"laptop:999.99:5,mouse:25.50:20,keyboard:75.00:15,monitor:299.99:8"
  • 次の形式の更新操作を含む文字列:"operation1:product1:value1,operation2:product2:value2"。ここで、操作は "price" または "quantity" のいずれかです(例:"price:laptop:899.99,quantity:mouse:25,price:keyboard:65.00"

あなたのタスクは以下の通りです:

  1. 以下のフィールドを持つ Product 構造体を定義します:
    • Price (float64) - 製品の価格
    • Quantity (int) - 製品の在庫数
  2. キーが製品名(文字列)で、値が Product 構造体へのポインタであるマップを作成します。
  3. 2番目の入力から製品データを解析します:
    • 入力をカンマで分割して、個々の製品エントリを取得します。
    • 各エントリをコロンで分割して、名前、価格、数量を分離します。
    • 価格文字列を float64 に、数量文字列を整数に変換します。
    • Product 構造体を作成し、そのポインタをマップに格納します。
  4. ヘッダー "Initial Inventory:" と共に初期在庫を表示します。
  5. 各製品を次の形式で表示します:"[name]: $[price] (Stock: [quantity])"
  6. 3番目の入力から更新操作を解析して適用します:
    • 入力をカンマで分割して、個々の操作を取得します。
    • 各操作をコロンで分割して、操作タイプ、製品名、新しい値を取得します。
    • マップのポインタを介して、適切なフィールドを直接更新します。
    • 各更新を次の形式で出力します:"Updated [product]: [field] changed to [new_value]"
  7. ヘッダー "Updated Inventory:" と共に更新後の在庫を表示します。
  8. 初期表示と同じ形式で各製品を再度表示します。
  9. 在庫の総額を計算して表示します:"Total Inventory Value: $[total_value]"

価格と数量の文字列をそれぞれの数値型に変換するには、strconv パッケージを使用してください。表示する際は、すべての価格を小数点以下2桁にフォーマットしてください。このチャレンジは、マップにポインタを格納することで、マップに値を再代入することなく、元の構造体データを直接変更し、在庫の更新を即座かつ永続的に行う方法を示しています。

チートシート

マップは構造体へのポインタを格納することができ、マップを通じて元の構造体データを直接変更することが可能です。

type Product struct {
    Price    float64
    Quantity int
}

products := map[string]*Product{
    "laptop": &Product{Price: 999.99, Quantity: 5},
    "mouse":  &Product{Price: 25.50, Quantity: 20},
}

マップの値としてポインタを使用する場合、マップを介した変更は元の構造体に影響を与えます。

// マップを通じて直接ラップトップの価格を更新します
products["laptop"].Price = 899.99
fmt.Println(products["laptop"].Price) // 出力: 899.99

このパターンは、在庫管理システムやユーザープロファイルなど、頻繁なデータ更新が必要なアプリケーションにおいて、マップが主要なデータストアとして機能する場合に非常に有用です。

自分で試してみよう

package main

import (
	"fmt"
	"strconv"
	"strings"
)

func main() {
	// 入力を読み込む
	var numProductsStr string
	var productDataStr string
	var operationsStr string
	
	fmt.Scanln(&numProductsStr)
	fmt.Scanln(&productDataStr)
	fmt.Scanln(&operationsStr)
	
	// TODO: ここで Product 構造体を定義する
	
	// TODO: 商品のポインタを格納するためのマップを作成する
	
	// TODO: 商品の順序を保持するためのスライスを作成する(一貫した出力を保証するため)
	
	// TODO: 商品データを解析し、マップにデータを入力する
	// 注意点:
	// - productDataStr をカンマで分割して個々のエントリを取得する
	// - 各エントリをコロンで分割して、名前、価格、数量を取得する
	// - 価格と数量の文字列を適切な型に変換する
	// - Product 構造体へのポインタをマップに保存する
	// - 商品名を順序保持用スライスに追加する
	
	// TODO: 初期の在庫を表示する
	// 商品を一定の順序で反復処理するためにスライスを使用する
	// フォーマット: "[name]: $[price] (Stock: [quantity])"
	
	// TODO: 更新操作を解析して適用する
	// 注意点:
	// - operationsStr をカンマで分割して個々の操作を取得する
	// - 各操作をコロンで分割して、タイプ、名前、値を取得する
	// - マップのポインタを介して適切なフィールドを直接更新する
	// - 各操作の更新メッセージを表示する
	
	// TODO: 更新された在庫を表示する
	// 再度、一貫した出力のために順序保持用スライスを使用する
	// 表示しながら合計金額を計算する
	
	// TODO: 在庫の総額を計算して表示する
	// フォーマット: "Total Inventory Value: $[total_value]"
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

論理とフローのすべてのレッスン