How to combine two arrays to use with ToObject()?

I’m trying to write a UDF to map the result from an index with values to an object.

So instead of returning the index array with values that looks something like:

['302130074358907392', 'John', 42]

I would like to return an object:

{
  id: '302130074358907392',
  name: 'John',
  age: 42
}

I have obtained the names of the fields from the index document in an array like this:

['id', 'name', 'age']

Now I’d like to use ToObject() to create an object from both arrays (values, and field names) but I’d need to create this array of arrays:

[['id', '302130074358907392'], ['name', 'John'], ['age', 42]]

I’m stuck because I haven’t been able to find a function to iterate an array and also get the index of the current iterator.

Anyone has any tips on how to solve this?

If the order of your results are constant, you can use Select to pull them out and use Let to place them inside of a new object.

Let(
    {
      arrResult: ['302130074358907392', 'John', 42],
      arrToObject: {
          id: Select(0, Var("arrResult")),
          name: Select(1, Var("arrResult")),
          age: Select(2, Var("arrResult"))
       }
    },
    Var("arrToObject")
  )

result:

{
  id: '302130074358907392',
  name: 'John',
  age: 42
}

In place the literal array value at arrResult, you can put the FQL function that gives you back your array from your index. ie. Paginate(Match(Index… etc

That’s the thing, it’s not constant.

In this example there are 3 values, but there could be N number of values.

So this took some time to figure out. The real problem here is keeping track of the indexes between the arrays. Thats why I would rather opt to restructure how the data is stored because this is a lot of work just to make an object. However, I have taken the time to figure out how to solve this if that is not possible for you.

Let(
    {
      firstArr: ['302130074358907392', 'John', 42],
      secondArr: ['id', 'name', 'age'],
      arrWithInd: Reduce(
        Lambda(
            ["acc", "val"],
            Append([[Var("val"), Count(Var("acc"))]], Var("acc"))
        ),
        [],
        Var("firstArr")
    ),
    combinedArrs: Map(
      Var("arrWithInd"), 
      Lambda(
        "tupleArr", 
        Append(
          [Select(0, Var("tupleArr"))], 
          [Select( 
             Select(1, Var("tupleArr")), 
             Var("secondArr")
           )
          ]
        )
      )
    ),
    arrToObj: ToObject(Var("combinedArrs")) 
  },
  Var("arrToObj")
)

result:

{
  id: '302130074358907392',
  name: 'John',
  age: 42
}

Okay so what is going on here? In order to keep track of the indexes, I made an array of arrays that stores the index as the second value in the arrWithInd variable using a helper FQL library I found on Github called WithIndex. The source code is in the src/functions folder. GitHub - shiftx/faunadb-fql-lib

In this case, it would translate to
[['302130074358907392', 0], ['John', 1], [42, 2]]
Once you have the indexes, the rest is relatively straight forward. Combining the arrays just becomes a matter of mapping over the array with your index and then using that index to dynamically select the value from the other array. Since the values in the array of tuples are known, I am using Select(0, …) and Select(1, …) where Select(0, …) is the value and Select(1,…) is the index of that value.

Then to get the final array of arrays back, I used Append to I just store the values into two array literals to get back the structure that you want. And the last step is using ToObject() as you requested to turn that array of arrays back into an object.

Now I will note that there is still a fundamental assumption baked into this which is that both of the arrays are the same length. I would presume they would have to be if you are trying to make key value pairs of each corresponding element in both arrays. I would put a guard to check if this were true. It might look something like this

If(
 Equals(Length(Var("firstArr")), Length(Var("secondArr")),
 Let(...),
 Abort("Array lengths are not equal.")
)

There may be an easier way to do this, but I haven’t seen a way to loop through both arrays at the same time and keep track of the individual elements from each array simultaneously (in which case you would just append them to an array without the step of knowing their index).

4 Likes

Thanks @kdilla301 !

Using Reduce() to extract the index is the trick I was missing.

1 Like

No problem. Make sure to check out that repo for other functions with specific functionality not currently found in FQL if you run into any other edge cases for how you need to manipulate some data.

1 Like

Here’s a gist with the function I made.

It’s a function that receives a reference to an index with values and the result of that index.

Let(
	{
		indexRef: Index("Index_with_values"),
		values: Select(
			"data",
			Paginate(Match(Var('indexDoc')))
		)
	},
	Call(Function("GetIndexValuesObject"), [
		Var('indexRef'),
		Var('values')
	])
)

So instead of getting an array with arrays of values:

[
  ['302130074358907392', 'John', 42],
  // etc
]

You’ll get an array of objects with the names of the fields as keys of the object:

[
  {
    id: '302130074358907392',
    name: 'John',
    age: 42
  },
  // etc
]
1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.