2
1

More than 3 years have passed since last update.

うちの本棚のレイアウトの最適化をGopherさんに考えてもらった

Posted at

本が増えたときに本棚にきちんと入れるためのレイアウトを考えるのが大変なので、Gopherに頑張ってもらうようにした

イメージ図

gophercomplex5_0.png

設計

本棚のサイズはIKEAのBILLYをベースにしています。
漫画だと1段に2列並べられて、1列22冊合計で44冊を1段に収納できました。

以降、1列をgroup、1段をrackと称することにします。

見送ったこと

出版社やブランドをまとめようと思いましたが、1日で作り上げるレベルではなさそうなのでやめました

作ったもの

データ

CSVで所蔵データを作って、これをベースに本が棚に収まるようにグルーピングします。
以下がCSVの例です

Title,Count,Brand,Maker
ダンジョン飯,8,Beam Comix,ハルタ
はたらく細胞,5,シリウスKC,講談社
テラフォーマーズ,5,ヤングジャンプコミックス,集英社
...

コード

出力は、とりあえずわかればいいので、 spew.Dump を使いました。

package main

import (
    "os"
    "sort"

    "github.com/davecgh/go-spew/spew"
    "github.com/gocarina/gocsv"
)

type book struct {
    Title string `csv:"Title"` // 本のタイトル
    Count int    `csv:"Count"` // 同タイトルの所持数
    Brand string `csv:"Brand"` // 本のブランド (ヤングジャンプコミックスとか)
    Maker string `csv:"Maker"` // 本の出版社 (講談社とか)
}

type rack struct {
    Total int
    Items []book
}

var (
    // 1groupの最大数
    groupMax = 22
    // 1rackの最大数
    rackMax  = 44
)

func main() {
    books := []*book{}
    if err := loadCSVFile("books.csv", &books); err != nil {
        panic(err)
    }

    sort.Slice(books, func(i, j int) bool {
        return books[i].Count > books[j].Count
    })

    racks := []rack{}
    for _, b := range books {
        items := []book{}
        // なるべくgroup単位にしたいけど、所蔵数がgroupMaxを超えることがあるので、その場合は別のrackに入れる
        if b.Count > groupMax {
            items = append(items, *b)
            racks = append(racks, rack{
                Total: b.Count,
                Items: items,
            })
        } else {
            var isSet bool
            for i, r := range racks {
                if r.Total >= rackMax {
                    continue
                }
                // 実際に入れるときに本棚の前後にまたがることの防止策
                if r.Total >= groupMax && r.Total+b.Count > rackMax {
                    continue
                }
                r.Total += b.Count
                r.Items = append(r.Items, *b)
                racks[i] = r
                isSet = true
                break
            }
            if !isSet {
                items = append(items, *b)
                racks = append(racks, rack{
                    Total: b.Count,
                    Items: items,
                })
            }
        }
    }
    spew.Dump(racks)
}

func loadCSVFile(fp string, v interface{}) (err error) {
    f, err := os.OpenFile(fp, os.O_RDWR|os.O_CREATE, os.ModePerm)
    if err != nil {
        return
    }
    defer f.Close()
    return gocsv.UnmarshalFile(f, v)
}

実行結果

([]main.rack) (len=4 cap=4) {
 (main.rack) {
  Total: (int) 43,
  Items: ([]main.book) (len=3 cap=4) {
   (main.book) {
    Title: (string) (len=24) "大使閣下の料理人",
    Count: (int) 25,
    Brand: (string) (len=17) "モーニングKC",
    Maker: (string) (len=9) "講談社"
   },
   (main.book) {
    Title: (string) (len=9) "グラメ",
    Count: (int) 13,
    Brand: (string) (len=24) "バンチコミックス",
    Maker: (string) (len=9) "新潮社"
   },
   (main.book) {
    Title: (string) (len=24) "テラフォーマーズ",
    Count: (int) 5,
    Brand: (string) (len=36) "ヤングジャンプコミックス",
    Maker: (string) (len=9) "集英社"
   }
  }
 },
 (main.rack) {
  Total: (int) 44,
  Items: ([]main.book) (len=3 cap=4) {
   (main.book) {
    Title: (string) (len=18) "華麗なる食卓",
    Count: (int) 24,
    Brand: (string) (len=36) "ヤングジャンプコミックス",
    Maker: (string) (len=9) "集英社"
   },
   (main.book) {
    Title: (string) (len=18) "信長のシェフ",
    Count: (int) 12,
    Brand: (string) (len=24) "芳文社コミックス",
    Maker: (string) (len=9) "芳文社"
   },
   (main.book) {
    Title: (string) (len=18) "ダンジョン飯",
    Count: (int) 8,
    Brand: (string) (len=10) "Beam Comix",
    Maker: (string) (len=9) "ハルタ"
   }
  }
 },
 (main.rack) {
  Total: (int) 44,
  Items: ([]main.book) (len=4 cap=4) {
   (main.book) {
    Title: (string) (len=18) "バーテンダー",
    Count: (int) 21,
    Brand: (string) (len=39) "ジャンプコミックデラックス",
    Maker: (string) (len=9) "集英社"
   },
   (main.book) {
    Title: (string) (len=21) "王様の仕立て屋",
    Count: (int) 10,
    Brand: (string) (len=39) "ジャンプコミックデラックス",
    Maker: (string) (len=9) "集英社"
   },
   (main.book) {
    Title: (string) (len=24) "異世界居酒屋のぶ",
    Count: (int) 9,
    Brand: (string) (len=33) "角川コミックス・エース",
    Maker: (string) (len=12) "角川書店"
   },
   (main.book) {
    Title: (string) (len=45) "とんでもスキルで異世界放浪メシ",
    Count: (int) 4,
    Brand: (string) (len=24) "ガルドコミックス",
    Maker: (string) (len=21) "オーバーラップ"
   }
  }
 },
 (main.rack) {
  Total: (int) 36,
  Items: ([]main.book) (len=8 cap=8) {
   (main.book) {
    Title: (string) (len=15) "君のナイフ",
    Count: (int) 7,
    Brand: (string) (len=39) "ジャンプコミックデラックス",
    Maker: (string) (len=9) "集英社"
   },
   (main.book) {
    Title: (string) (len=18) "はたらく細胞",
    Count: (int) 5,
    Brand: (string) (len=14) "シリウスKC",
    Maker: (string) (len=9) "講談社"
   },
   (main.book) {
    Title: (string) (len=27) "まどろみバーメイド",
    Count: (int) 5,
    Brand: (string) (len=24) "芳文社コミックス",
    Maker: (string) (len=9) "芳文社"
   },
   (main.book) {
    Title: (string) (len=1) "G",
    Count: (int) 5,
    Brand: (string) (len=36) "ヤングサンデーコミックス",
    Maker: (string) (len=9) "小学館"
   },
   (main.book) {
    Title: (string) (len=12) "死刑囚042",
    Count: (int) 5,
    Brand: (string) (len=36) "ヤングジャンプコミックス",
    Maker: (string) (len=9) "集英社"
   },
   (main.book) {
    Title: (string) (len=27) "干物妹うまるちゃん",
    Count: (int) 3,
    Brand: (string) (len=36) "ヤングジャンプコミックス",
    Maker: (string) (len=9) "集英社"
   },
   (main.book) {
    Title: (string) (len=9) "電車男",
    Count: (int) 3,
    Brand: (string) (len=36) "ヤングサンデーコミックス",
    Maker: (string) (len=9) "小学館"
   },
   (main.book) {
    Title: (string) (len=12) "源君物語",
    Count: (int) 3,
    Brand: (string) (len=36) "ヤングジャンプコミックス",
    Maker: (string) (len=9) "集英社"
   }
  }
 }
}

後記

実際に本棚を並べ替えてみると、1冊のサイズの関係もあって、結果通りにいかないところもありましたが、ほぼほぼ問題ありませんでした。
1冊あたりの厚さ計算して並べ替えたほうがBetterっぽいです。

2
1
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
2
1