Reduce

This reference topic applies to FQL v4. Go to this page for the latest FQL v10 reference topics.

Reduce( reducer, initial, arrayOrSet )
Reduce( reducer, initial, arrayOrSet )
Reduce( reducer, initial, arrayOrSet )
reduce( reducer, initial, arrayOrSet )
Reduce( reducer, initial, arrayOrSet )

Description

The Reduce function applies a reducer Lambda function serially to each member of arrayOrSet (which is an Array, Page, or Set) to produce a single Value (any Scalar, Array, Object, etc.).

When the reducer function is called, it is provided with an accumulator that represents the current state of the result, and the current item from arrayOrSet. The value that the reducer returns becomes the accumulator for the next iteration. Subsequent invocations of reducer can see the results of earlier invocations.

When Reduce starts, the accumulator is set to the initial value. When Reduce completes its processing, the current value of the accumulator is returned.

The run time of Reduce is dependent on the number of elements in the underlying set or page — it’s linear, or O(n). For very large sets or pages, executing Reduce might result in a query timeout error, or "width" error.

For query "width" errors, the underlying set or page involves more than 100K items. This can happen when using a set function, such as Difference, where more than 100K items need to be considered to produce the set that Reduce evaluates. To resolve this, use Paginate to limit the set or page size.

For example, instead of:

Reduce(
  Difference(
    Match(Index("Index1"), "term1"),
    Match(Index("Index2"), "term2")
  )
)

use:

Reduce(
  Paginate(
    Difference(
      Match(Index("Index1"), "term1"),
      Match(Index("Index2"), "term2")
    ),
    { size: 10000 }
  )
)

This does mean that if the entire set must be evaluated to arrive at the correct result, you would have to page through the Paginate results.

For query timeout errors, you may specify a larger query timeout via the driver that you are using.

Parameters

Parameter Type Definition and Requirements

reducer

The function to apply to each item in arrayOrSet.

The function must have the signature f( accumulator, value ), where:

  • accumulator is the current state of the result

  • value is the current item from arrayOrSet

The return value from the reducer function becomes the accumulator for the next iteration.

initial

Any

The initial parameter sets the initial value of the accumulator.

arrayOrSet

Array, Page, or Set

The items to be reduced.

Returns

The value of the accumulator, which would typically be the same type as the initial value.

Examples

The following query demonstrates the simplest reducer, which counts the items in arrayOrSet. It does this by adding 1 to the accumulator for each item:

try
{
    Value result = await client.Query(
        Reduce(
            Lambda(Arr("acc", "value"), Add(Var("acc"), 1)),
            0,
            Arr(1, 2, 3, 4, 5)
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
LongV(5)
result, err := client.Query(
	f.Reduce(
		f.Lambda(
			f.Arr{"acc", "value"},
			f.Add(f.Var("acc"), 1)),
		0,
		f.Arr{1, 2, 3, 4, 5}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
5
client.query(
  q.Reduce(
    q.Lambda(
      ['acc', 'value'],
      q.Add(q.Var('acc'), 1),
    ),
    0,
    [ 1, 2, 3, 4, 5 ],
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
5
result = client.query(
  q.reduce(
    q.lambda_(["acc", "value"], q.add(q.var("acc"), 1)),
    0,
    [1, 2, 3, 4, 5],
  )
)
print(result)
5
Reduce(
  Lambda(
    ["acc", "value"],
    Add(Var("acc"), 1)
  ),
  0,
  [ 1, 2, 3, 4, 5 ],
)
5
Query metrics:
  •    bytesIn: 107

  •   bytesOut:  14

  • computeOps:   1

  •    readOps:   0

  •   writeOps:   0

  •  readBytes:   0

  • writeBytes:   0

  •  queryTime: 4ms

  •    retries:   0

The following query demonstrates how to sum the values in arrayOrSet. It does this by adding the value of each item to the accumulator:

try
{
    Value result = await client.Query(
        Reduce(
            Lambda(Arr("acc", "value"), Add(Var("acc"), Var("value"))),
            0,
            Arr(1, 2, 3, 4, 5)
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
LongV(15)
result, err := client.Query(
	f.Reduce(
		f.Lambda(
			f.Arr{"acc", "value"},
			f.Add(f.Var("acc"), f.Var("value")),
		),
		0,
		f.Arr{1, 2, 3, 4, 5}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
15
client.query(
  q.Reduce(
    q.Lambda(
      ['acc', 'value'],
      q.Add(q.Var('acc'), q.Var('value'))
    ),
    0,
    [ 1, 2, 3, 4, 5 ]
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
15
result = client.query(
  q.reduce(
    q.lambda_(["acc", "value"], q.add(q.var("acc"), q.var("value"))),
    0,
    [1, 2, 3, 4, 5],
  )
)
print(result)
15
Reduce(
  Lambda(
    ["acc", "value"],
    Add(Var("acc"), Var("value"))
  ),
  0,
  [ 1, 2, 3, 4, 5 ]
)
15
Query metrics:
  •    bytesIn: 121

  •   bytesOut:  15

  • computeOps:   1

  •    readOps:   0

  •   writeOps:   0

  •  readBytes:   0

  • writeBytes:   0

  •  queryTime: 3ms

  •    retries:   0

The following query demonstrates how to count, sum, and compute the minimum and maximum values, plus the average. This time, our accumulator is an object with multiple keys, and our Lambda function now performs all of those calculations:

try
{
    Value result = await client.Query(
        Reduce(
            Lambda(
                Arr("acc", "value"),
                Let(
                    "count", Add(Select("count", Var("acc")), 1),
                    "total", Add(Select("total", Var("acc")), Var("value")),
                    "min", Select("min", Var("acc")),
                    "max", Select("max", Var("acc"))
                ).In(
                    Obj(
                        "count", Var("count"),
                        "total", Var("total"),
                        "min", If(
                            LTE(Var("value"), Var("min")),
                            Var("value"),
                            Var("min")
                        ),
                        "max", If(
                            GTE(Var("value"), Var("max")),
                            Var("value"),
                            Var("max")
                        ),
                        "avg", Divide(Var("total"), Var("count"))
                    )
                )
            ),
            Obj(
                "count", 0,
                "total", 0,
                "min", 999999,
                "max", -999999,
                "avg", 0
            ),
            Arr(1, 2, 3, 4, 5)
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(count: LongV(5),total: LongV(15),min: LongV(1),max: LongV(5),avg: LongV(3))
result, err := client.Query(
	f.Reduce(
		f.Lambda(
			f.Arr{"acc", "value"},
			f.Let().Bind(
				"count", f.Add(
					f.Select("count", f.Var("acc")),
					1,
				)).Bind(
				"total", f.Add(
					f.Select("total", f.Var("acc")),
					f.Var("value"),
				)).Bind(
				"min", f.Select("min", f.Var("acc"))).Bind(
				"max", f.Select("max", f.Var("acc"))).In(
				f.Obj{
					"count": f.Var("count"),
					"total": f.Var("total"),
					"min": f.If(
						f.LTE(f.Var("value"), f.Var("min")),
						f.Var("value"),
						f.Var("min")),
					"max": f.If(
						f.GTE(f.Var("value"), f.Var("max")),
						f.Var("value"),
						f.Var("max")),
					"avg": f.Divide(f.Var("total"), f.Var("count")),
				})),
		f.Obj{
			"count": 0,
			"total": 0,
			"min": 99999,
			"max": -99999,
			"avg": 0,
		},
		f.Arr{1, 2, 3, 4, 5},
	))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[avg:3 count:5 max:5 min:1 total:15]
client.query(
  q.Reduce(
    q.Lambda(
      ['acc', 'value'],
      q.Let(
        {
          count: q.Add(q.Select('count', q.Var('acc')), 1),
          total: q.Add(q.Select('total', q.Var('acc')), q.Var('value')),
          min: q.Select('min', q.Var('acc')),
          max: q.Select('max', q.Var('acc')),
        },
        {
          count: q.Var('count'),
          total: q.Var('total'),
          min: q.If(
            q.LTE(q.Var('value'), q.Var('min')),
            q.Var('value'),
            q.Var('min'),
          ),
          max: q.If(
            q.GTE(q.Var('value'), q.Var('max')),
            q.Var('value'),
            q.Var('max')
          ),
          avg: q.Divide(q.Var('total'), q.Var('count')),
        }
      )
    ),
    {
      count: 0,
      total: 0,
      min: 999999,
      max: -999999,
      avg: 0,
    },
    [ 1, 2, 3, 4, 5 ]
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{ count: 5, total: 15, min: 1, max: 5, avg: 3 }
result = client.query(
  q.reduce(
    q.lambda_(["acc", "value"], q.let(
      {
        "count": q.add(q.select("count", q.var("acc")), 1),
        "total": q.add(q.select("total", q.var("acc")), q.var("value")),
        "min": q.select("min", q.var("acc")),
        "max": q.select("max", q.var("acc")),
      },
      {
        "count": q.var("count"),
        "total": q.var("total"),
        "min": q.if_(
          q.lte(q.var("value"), q.var("min")),
          q.var("value"),
          q.var("min")
        ),
        "max": q.if_(
          q.gte(q.var("value"), q.var("max")),
          q.var("value"),
          q.var("max")
        ),
        "avg": q.divide(q.var("total"), q.var("count")),
      }
    )),
    {
      "count": 0,
      "total": 0,
      "min": 999999,
      "max": -999999,
      "avg": 0,
    },
    [1, 2, 3, 4, 5]
  )
)
print(result)
{'count': 5, 'total': 15, 'min': 1, 'max': 5, 'avg': 3}
Reduce(
  Lambda(
    ["acc", "value"],
    Let(
      {
        count: Add(Select("count", Var("acc")), 1),
        total: Add(Select("total", Var("acc")), Var("value")),
        min: Select("min", Var("acc")),
        max: Select("max", Var("acc")),
      },
      {
        count: Var("count"),
        total: Var("total"),
        min: If(LTE(Var("value"), Var("min")), Var("value"), Var("min")),
        max: If(GTE(Var("value"), Var("max")), Var("value"), Var("max")),
        avg: Divide(Var("total"), Var("count")),
      }
    )
  ),
  {
    count: 0,
    total: 0,
    min: 999999,
    max: -999999,
    avg: 0,
  },
  [ 1, 2, 3, 4, 5 ]
)
{ count: 5, total: 15, min: 1, max: 5, avg: 3 }
Query metrics:
  •    bytesIn:  698

  •   bytesOut:   59

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    0

  •  readBytes:    0

  • writeBytes:    0

  •  queryTime: 17ms

  •    retries:    0

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!