複数の一致条件によって無限にネストされたオブジェクトの配列を再帰的にフィルタリングしますが、両方の一致のインスタンスを持つ親のみを返します

3
2022.01.13

私は次のオブジェクトの配列を持っています。しかし、これは未知のキー/値であり、無限にネストされる可能性があり、今のところこれはテストサンプルです:

[
  {
    "reference_id": "R123",
    "customer": "Person 1",
    "customer_email": "[email protected]",
    "location": "UK",
    "bookings": [
      {
        "product": "Product 1",
        "provider": "Company 1",
        "cancellable": true
      },
      {
        "product": "Product 2",
        "provider": "Company 2",
        "cancellable": true
      },
      {
        "product": "Product 3",
        "provider": "Company 1",
        "cancellable": true
      }
    ]
  },
  {
    "reference_id": "R1234",
    "customer": "Person 2",
    "customer_email": "[email protected]",
    "location": "USA",
    "bookings": [
      {
        "product": "Product 1",
        "provider": "Company 1",
        "cancellable": true
      },
      {
        "product": "Product 3",
        "provider": "Company 1",
        "cancellable": true
      }
    ]
  },
  {
    "reference_id": "R12345",
    "customer": "Person 3",
    "customer_email": "[email protected]",
    "location": "UK",
    "bookings": [
      {
        "product": "Product 2",
        "provider": "Company 2",
        "cancellable": true
      },
      {
        "product": "Product 3",
        "provider": "Company 1",
        "cancellable": true
      }
    ]
  }
]

現在の実装は次のとおりです。

const selected = [
  {
    term: 'Company 1',
    column: 'provider',
  },
  {
    term: 'Person 1',
    column: 'customer',
  },
];

const recursivelyFilterByValue = () => (value) => selected.every((item) => {
  if (!value) return false;

  if (typeof value === 'string') {
    // console.log('value', value === item.term);
    return value === item.term;
  }

  if (Array.isArray(value)) {
    return value.some(this.recursivelyFilterByValue());
  }

  if (typeof value === 'object') {
    return Object.values(value).some(this.recursivelyFilterByValue());
  }

  return false;
});

const results = data.filter(recursivelyFilterByValue());

基本的に私は"選択された"配列に追加し、それを使用してデータ配列をフィルタリングします。私はキーが"列"と一致することを保証したいと思っていますが、まだ追加していません。

上記の入力については、以下を出力することを期待します。

[
  {
    "reference_id": "R123",
    "customer": "Person 1",
    "customer_email": "[email protected]",
    "location": "UK",
    "bookings": [
      {
        "product": "Product 1",
        "provider": "Company 1",
        "cancellable": true
      },
      {
        "product": "Product 2",
        "provider": "Company 2",
        "cancellable": true
      },
      {
        "product": "Product 3",
        "provider": "Company 1",
        "cancellable": true
      }
    ]
  },
]

ただし、出力配列は空です。1 つの用語だけを検索する (選択した配列から 1 つの用語を除くすべてを削除する) 場合、その用語の出力は正しいが、後続の用語は空白の配列を取り戻します。

私の.some()の使用が問題であるかどうか疑問に思っていますが、これを変更すると再帰エラーが多すぎます。

基本的に、選択した配列内のすべての条件に対して key:value が一致している限り、その子の任意のレベルで、元の親オブジェクトを返したいと思います。

どんな指導も感謝します、ありがとう。

回答
4
2022.01.13

これがあなたが探しているのかどうかはわかりません。それはコメントの私の推測が正しかったという推測を作ります:

私はこの権利を持っていますか?オブジェクトに値"Customer 1"を持つproviderプロパティがあるか、または (再帰的に) 下位オブジェクトを持つという (おそらく動的な) 条件があります。そして、customer"Person 1"に関する 2 番目の条件があり、そのような条件の両方 (またはすべて) を満たすオブジェクトを探しています。それはあなたが何をしようとしているのかを説明していますか?

ここでは、2つのかなり単純なヘルパー関数 、testRecursivemakePredicates だけでなく、メイン関数 、recursivelyFilterByValueがあります。

const testRecursive = (pred) => (obj) => 
  pred (obj) || Object (obj) === obj && Object .values (obj) .some (testRecursive (pred))

const makePredicates = (criteria) => 
  criteria .map (({term, column}) => (v) => v [column] == term)

const recursivelyFilterByValue = (criteria, preds = makePredicates (criteria)) => (xs) =>
  xs .filter (obj => preds .every (pred => testRecursive (pred) (obj)))


const selected = [{term: 'Company 1', column: 'provider'}, {term: 'Person 1', column: 'customer'}]

const input = [{reference_id: "R123", customer: "Person 1", customer_email: "[email protected]", location: "UK", bookings: [{product: "Product 1", provider: "Company 1", cancellable: true}, {product: "Product 2", provider: "Company 2", cancellable: true}, {product: "Product 3", provider: "Company 1", cancellable: true}]}, {reference_id: "R1234", customer: "Person 2", customer_email: "[email protected]", location: "USA", bookings: [{product: "Product 1", provider: "Company 1", cancellable: true}, {product: "Product 3", provider: "Company 1", cancellable: true}]}, {reference_id: "R12345", customer: "Person 3", customer_email: "[email protected]", location: "UK", bookings: [{product: "Product 2", provider: "Company 2", cancellable: true}, {product: "Product 3", provider: "Company 1", cancellable: true}]}]

console .log (recursivelyFilterByValue (selected) (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

  • testRecursive は、述部がオブジェクトに対して true であるか、またはその中にネストされているオブジェクトに対して true であるかをチェックします。

  • makePredicates は 、{term, column}オブジェクトの配列を述語関数に変換し、オブジェクトが列で指定されたプロパティに適切な項を持っているかどうかをテストします。

  • recursivelyFilterByValue はこれらを組み合わせて 、makePredicates を呼び出して選択した項目を述語関数に変換し、各述語が真であるかどうかをテストして入力をフィルター処理します。

これは、想像できる最も効率的なコードではありません。各述語の階層を再スキャンします。私は一度だけスキャンを行うためのバージョンを理解できると確信していますが、私はそれがはるかに複雑なコードのために作ると思います。そのため、ニーズに十分な速度を持つかどうかを、実稼働サイズのデータでテストする必要があります。