> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/web/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/web/js/val/class/example/bag.md).

# Bag

keeps track of items and its count.

[JS](/web/js.md) ⟩ [value](/web/js/val.md) ⟩ [object](/web/js/val/obj.md) ⟩ [class](/web/js/val/class.md) ⟩ [example](/web/js/val/class/example.md) ⟩ Bag&#x20;

{% hint style="success" %}
create a new class <mark style="color:red;">**not**</mark>**&#x20;**<mark style="color:yellow;">**by subclassing**</mark>, but instead by <mark style="color:yellow;">**wrapping**</mark> or “<mark style="color:yellow;">**composing**</mark>” other classes, this <mark style="color:purple;">**delegation**</mark> approach is often called “<mark style="color:purple;">**composition**</mark>”.
{% endhint %}

{% tabs %}
{% tab title="💾 程式" %}

* replit ⟩ Bag ([JS](https://replit.com/@pegasusroe/Bag-js#Bag.js), [Swift](https://replit.com/@pegasusroe/Bag#Bag.swift))

```javascript
// ⭐️ Bag
// ------------------------------------------------
// create new class by delegation ("composition")
// ------------------------------------------------
// • .totalCount : total items count
// • .items      : all (different) items (iterator)
// • .counts     : all counts (iterator)
// • .entries    : all item/count pairs (iterator)
// ------------------------------------------------
// • .count(item): item count
// • .has(item)  : check if bag has item.
// • .put(item, [count])  : put item in bag.
// • .take(item, [count]) : take item from bag if exists.
class Bag {

    // private map
    #map = new Map();

    // Set-like methods
    has(item) { return this.count(item) > 0 }

    // ----------------------
    //     iterations
    // ----------------------

    get items() { return this.#map.keys() }         // iterator (for items)
    get counts() { return this.#map.values() }      // iterator (for counts)
    get entries() { return this.#map.entries() }    // iterator (item/count pairs)
    
    [Symbol.iterator]() { return this.entries }

    // ----------------------
    //     bag operations
    // ----------------------

    // item count
    count(item) {
        return this.#map.get(item) ?? 0;
    }

    // total count
    get totalCount() {
        return Array.from(this.counts)
            .reduce((result, value) => result + value, 0);
    }
    
    // put item(s) into bag
    put(item, count = 1) {
        this.#map.set(item, this.count(item) + count);
        return this;    // for chaining
    }

    // take item(s) from bag
    take(item, count = 1) {
        
        const left = this.count(item);

        if (left > count) {
            this.#map.set(item, left - count);
        } else if (left <= count) {
            this.#map.delete(item);
        }

        return this;    // for chaining
    }
}

// export
module.exports = Bag;
```

{% endtab %}

{% tab title="💈範例" %}

```javascript
const Bag = require('./Bag.js');

// test run
const bag = new Bag();

bag
    .put('apple', 2)
    .put('egg', 3)
    .put('stone')
    .take('egg', 2);

// test iterator
for (const [item, count] of bag) {
    log(`${item}: ${count}`);    // apple: 2, egg: 1, stone: 1
}

// ⭐ log
[
    bag.has('apple'),   // true
    bag.has('fish'),    // false
    bag.count('fish'),  // 0
    bag.totalCount,     // 4
    bag.entries,        // [ 'apple', 2 ], [ 'egg', 1 ], [ 'stone', 1 ]
    
].forEach(x => log(x));
```

{% endtab %}

{% tab title="📗 參考" %}

* [x] [JavaScript: The Definitive Guide](/web/master/ref/javascript-the-definitive-guide.md) ⟩ 9.5.3 Delegation Instead of Inheritance
  {% endtab %}
  {% endtabs %}
