1. ホーム
  2. ジャバスクリプト

[解決済み] ko.utils.arrayMap で、配列の一部しか返さない。

2022-03-04 04:09:03

質問

Knockoutのユーティリティ機能を使用しています。 http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html

ある条件に基づいて特定のプロパティを選択するarrayMapをしたい、など。

return ko.utils.arrayMap(myObservableArray(), function (item) {
    return item.Label;
});

例えば、これが次のような出力になったとする。

[null, "", "SomeLabel", null, "SomeOtherLabel"]

条件に基づいてプロパティを選択したいので、試してみます。

return ko.utils.arrayMap(myObservableArray(), function (item) {
    if (item.Label && item.Label !== "") {
        return item.Label;
    }
});

しかし、そうすると、次のような配列になってしまいます。

[undefined, undefined, "SomeLabel", undefined, "SomeOtherLabel"]

こんなこともやってみました。

return ko.utils.arrayMap(myObservableArray(), function (item) {
    return (item.Label && item.Label !== "") ? item.Label : false;
});

が、得られる。

[false, false, "SomeLabel", false, "SomeOtherLabel"]

だから、私はその後、しなければならないのです。

var itemsWithLabels = ko.utils.arrayFilter(myObservableArray(), function (item) {
    return (item.Label && item.Label !== "");
});
return ko.utils.arrayMap(itemsWithLabels, function (item) {
    return item.Label;
});

というのが出てきます。

["SomeLabel", "SomeOtherLabel"]

ko.utilsなどを使って一発で達成できる、より効率的な方法はないでしょうか?

どのように解決するのですか?

お気づきのように、ko.utils.arrayMapでは、コールバックが何かを返すことが期待されています。したがって、これは常にコールバックの戻り値を配列に追加する「ダム関数」です。未定義、ヌル、または偽を返しても、結果の配列から値を省略することはありません。

arrayFilter は、フィルタリングされた項目を変更することはできません。 元の項目が結果配列にプッシュされることになります。

つまり、ko.utils.array*の関数を使っても、これ以上の効率は望めないということです。それらを組み合わせて、コードをもう少し冗長にして、おそらくそれらを計算機に入れることもできます。

var itemsWithLabels = ko.computed(function () {
    return ko.utils.arrayMap(ko.utils.arrayFilter(myObservableArray(), function (item) {
        return item.Label && item.Label.length;
    }), function (filteredItem) {
        return filteredItem.Label;
    });
});

でも、これが精一杯なんですよね。最初にフィルタをかけて、その後にマッピングを行うという方法を選んだのは、マッピングの方がフィルタリングよりもコストがかかりそうだからです。でも、これは単なる勘ですけどね。

Underscoreのようなライブラリが、これを直接行うメソッドを提供しているのかもしれませんね。

また、このようなメソッドを自分で書くこともかなり簡単です(必要であればko.utilsに入れることも可能です)。

    ko.utils.arrayMapFilter = function (array, mapping) {
        array = array || [];
        var result = [], mapResult;
        for (var i = 0, j = array.length; i < j; i++) {
            mapResult = mapping(array[i]);
            if (mapResult) {
                result.push(mapResult);
            }
        }
        return result;
    },

マッピングコールバックは 0, "", false, null, undefined などの偽の値を返すことができるようになり、これらは配列に含まれなくなりました。

もし上記の値のうちのいくつかをとにかく許可したいのであれば(例えば0や"")、その行を変更するだけです。

if (mapResult)

のような、より厳密なものに変更します。

if (mapResult !== undefined && mapResult !== null)