Get one item from array matching one field

I have array of items
Distinction is that array have just two unique Refs repeated with each tag because there is array of tags in document.
Like so:

data: [
[1234, “asdf”, Ref(Collection(“products”), “276200545549550081”), “tag1”],
[1234, “asdf”, Ref(Collection(“products”), “276200545549550081”), “tag2”],
[1234, “asdf”, Ref(Collection(“products”), “276200545549550081”), “tag3”],
[null, “asdf2”, Ref(Collection(“products”), “287086223566045697”), “tag1”],
[null, “asdf2”, Ref(Collection(“products”), “287086223566045697”), “tag3”]
]

What i would like to do is to get just one result from this array for each Ref (no matter which tag).

Basically something like “Pick first matching by ref”.
So i would end up with just two items:

{ data:[
[1234, “asdf”, Ref(Collection(“products”), “276200545549550081”), “tag3”],
[null, “asdf2”, Ref(Collection(“products”), “287086223566045697”), “tag1”]
]
}

How would i do that in FQL?

I think this will do what you want. You need to use a combination of Reduce, Merge, ToObject and ToArray to get it to work:

Let({
  initialData: [
    [1234, 'asdf', Ref(Collection('product'), '276200545549550081'), 'tag1'],
    [1234, 'asdf', Ref(Collection('product'), '276200545549550081'), 'tag2'],
    [1234, 'asdf', Ref(Collection('product'), '276200545549550081'), 'tag3'],
    [null, 'asdf2', Ref(Collection('product'), '287086223566045697'), 'tag1'],
    [null, 'asdf2', Ref(Collection('product'), '287086223566045697'), 'tag3']
  ],
  uniqueRefsDictionary: Reduce(
    Lambda(
      ['accumulator', 'arrayItem'],
      Merge(
        Var('accumulator'),
        Let({
          ref: Select([2], Var('arrayItem')),
          // refstring holds ref converted to string Ref(Collection('product'), '287086223566045697') -> '287086223566045697'
          refString: ToString(Select(['id'], Var('ref'))),
        },
          // this will let us create a new object where the key is the refString, and the value is the original array item 
          // so it will convert [1234, 'asdf', Ref(Collection('product'), '276200545549550081'), 'tag1'] to {276200545549550081: [1234, 'asdf', Ref(Collection('product'), '276200545549550081'), 'tag1']}
          ToObject([[Var('refString'), Var('arrayItem')]])
        )
      )
      // the merge operation will keep overwriting any existing array with the same ref key so there will only be one entry per ref
    ),
    {},
    Var('initialData')
  ),
},
// Now the refs are stored as an object like {276200545549550081: [1234, 'asdf', Ref(Collection('product'), '276200545549550081'), 'tag3'], 287086223566045697: [null, 'asdf2', Ref(Collection('product'), '287086223566045697'), 'tag3']} 
// so we have to convert back into an array. So we convert that object into an array like [['276200545549550081', [1234, 'asdf', Ref(Collection('product'), '276200545549550081'), 'tag3']], ['287086223566045697', [null, 'asdf2', Ref(Collection('product'), '287086223566045697'), 'tag3']]
// and just grab index 1 from each of those, which is the original array item
  Map(
    ToArray(Var('uniqueRefsDictionary')),
    Lambda('keyValuePairArray', Select([1], Var('keyValuePairArray')))
  )
)

That will return this result:

[
  [1234, "asdf", Ref(Collection("product"), "276200545549550081"), "tag3"],
  [null, "asdf2", Ref(Collection("product"), "287086223566045697"), "tag3"]
]

If you did want to do some sorting or had a preference for which array you wanted per-reference, you could probably use the customResolver option in Merge to accomplish that:

Or you could just presort the data so that whichever one you want it to end up with naturally comes last for each group of references.

2 Likes

Are these values the result of an index?

If you want just the unique values without the tag, use an index that doesn’t include tag as a value and then wrap in Distinct. If the tag doesn’t matter, why search for it? You may have a good reason! Maybe you can get it later, though?

1 Like

That’s the solution.

Yes, values are returned from an index.

I search for documents by combination of tags:

 Paginate(  Distinct(
   Intersection(
     Match("pt13",'tag1'),
     Match("pt13",'tag3'),
     )
   ) 
)

I was returning “tags” as value and that caused multiple “Ref” entries (my first array in question) because array creates separate record in index for each item in this array.

I was assuming that “just existence” of array in document makes separate entries in index (regardless of that array being indexed in values or not).

So not including tags in values solves it.
Thanks!

1 Like