0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Day 2: 構造パスの基本 - データへの「住所」という考え方

Last updated at Posted at 2025-12-01

この記事は「構造パスで作るシンプルなフロントエンドフレームワーク (Structive)」Advent Calendarの2日目です。
Structiveについて詳しくはこちらより

前回のおさらい

1日目では、従来のフレームワークが抱える課題と、構造パスという新しいアプローチを紹介しました。今日は、その核となる「構造パス」の仕組みを深く掘り下げていきます。

構造パスとは何か?

構造パスは、階層的なデータ構造における特定の値の位置を、文字列で一意に表現したものです。

例えるなら、郵便の住所のようなものです:

  • 住所: "東京都渋谷区1-2-3" → 特定の建物を指す
  • 構造パス: "user.profile.name" → 特定のデータを指す

基本的な例

const data = {
  user: {
    profile: {
      name: "Alice",
      age: 25
    },
    settings: {
      theme: "dark",
      notifications: true
    }
  }
};

// 構造パス → 値
"user.profile.name"          // → "Alice"
"user.profile.age"           // → 25
"user.settings.theme"        // → "dark"
"user.settings.notifications" // → true

ドット(.)で区切られた各部分が、オブジェクトの階層を表しています。
JavaScriptのオブジェクト階層を表現するドット記法とほぼ変わりませんね。

ワイルドカードによる配列の抽象化

ちょっと違うのが、構造パスでは、配列の要素をワイルドカード(*)を使ってパスで表現してしまうところなんです。

配列データの例

const data = {
  users: [
    { name: "Alice", age: 25, city: "Tokyo" },
    { name: "Bob", age: 30, city: "Osaka" },
    { name: "Carol", age: 28, city: "Kyoto" }
  ]
};

ワイルドカード(*)の使い方

*を使うことで、配列の全要素を抽象的に表現できます:

// ワイルドカードを使った構造パス
"users.*.name"  // → すべてのユーザーの name を指す
"users.*.age"   // → すべてのユーザーの age を指す
"users.*.city"  // → すべてのユーザーの city を指す

// 実際には以下のように展開される
"users.0.name"  // → "Alice"
"users.1.name"  // → "Bob"
"users.2.name"  // → "Carol"

"users.0.age"  // → 25
"users.1.age"  // → 30
"users.2.age"  // → 28

"users.0.city"  // → "Tokyo"
"users.1.city"  // → "Osaka"
"users.2.city"  // → "Kyoto"

なぜワイルドカードが重要か?

UIテンプレートでは、配列の各要素に対して同じ操作を繰り返すことがよくあります:

<!-- すべてのユーザーの名前を表示したい -->
<ul>
  <li>Alice</li>
  <li>Bob</li>
  <li>Carol</li>
</ul>

ワイルドカードを使えば、これを抽象的に記述できます:

{{ for:users }}
  <li>{{ users.*.name }}</li>
{{ endfor: }}

ポイント: users.*.nameという一つの構造パスで、すべてのユーザーの名前を表現できます。

構造パスの実装イメージ

実際の実装では、構造パスを解析してデータにアクセスします。

基本的な get 関数

function get(obj, path) {
  const keys = path.split('.');
  let current = obj;
  
  for (const key of keys) {
    if (current == null) return undefined;
    current = current[key];
  }
  
  return current;
}

// 使用例
const data = { user: { profile: { name: "Alice" } } };
console.log(get(data, "user.profile.name")); // "Alice"

ワイルドカードを含む場合

function getAll(obj, path) {
  const keys = path.split('.');
  let current = [obj];
  
  for (const key of keys) {
    if (key === '*') {
      // 配列の全要素を展開
      current = current.flatMap(item => 
        Array.isArray(item) ? item : []
      );
    } else {
      current = current.map(item => item?.[key]);
    }
  }
  
  return current;
}

// 使用例
const data = {
  users: [
    { name: "Alice", age: 25 },
    { name: "Bob", age: 30 }
  ]
};
console.log(getAll(data, "users.*.name")); // ["Alice", "Bob"]

UIとの関係

構造パスの本当の威力は、UIと状態を結びつけるときに発揮されます。

同じ構造、同じパス

状態とUIが同じ構造を持つため、同じ構造パスでアクセスできます:

// 状態の定義
class State {
  product = {
    name: "Laptop",
    price: 999
  };
}
<!-- UIテンプレート -->
<div>
  <h2>{{ product.name }}</h2>
  <p>Price: ${{ product.price }}</p>
</div>

ここが重要: UIの{{ product.name }}と状態のproduct.nameは、まったく同じ構造パスです。

なぜこれが革新的なのか?

  1. 中間層が不要: UIと状態の間に変換層が不要
  2. 明示的な依存関係: {{ product.name }}を見れば、このUIがproduct.nameに依存していることが一目瞭然
  3. ピンポイントな更新: product.nameが変わったら、そのパスを使っているUIだけを更新すれば良い

構造パスの記法まとめ

構造パス 説明
object.property オブジェクトのプロパティ user.name
object.nested.property ネストしたプロパティ user.profile.age
array.*.property 配列の各要素のプロパティ users.*.name
array.* 配列の各要素 items.*

実践例:商品リスト

構造パスを使って、商品リストを表現してみましょう:

// 状態
class State {
  products = [
    { id: 1, name: "Laptop", price: 999 },
    { id: 2, name: "Mouse", price: 29 },
    { id: 3, name: "Keyboard", price: 79 }
  ];
}
<!-- UI -->
<ul>
  {{ for:products }}
    <li>
      {{ products.*.name }} - ${{ products.*.price }}
    </li>
  {{ endfor: }}
</ul>

展開されるHTML:

<ul>
  <li>Laptop - $999</li>
  <li>Mouse - $29</li>
  <li>Keyboard - $79</li>
</ul>

まとめ

今日は構造パスの基本を学びました:

構造パスとは:

  • データの位置を文字列で表現する「住所」
  • ドット記法でネストを表現
  • ワイルドカードで配列を抽象化

構造パスの利点:

  • データへの統一的なアクセス方法
  • 動的なパス構築が可能
  • UIと状態を同じパスで表現できる

次回予告:
明日は、「UIと状態を同じ構造で表現する思想」について、より深く掘り下げます。なぜこのアプローチがシンプルさとスケーラビリティを両立できるのか、具体例とともに解説します。


次回: Day 3「UIと状態を同じ構造で表現する思想」

構造パスについて質問があれば、ぜひコメント欄で!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?