Definición
$reduceAplica una expresión a cada elemento de un arreglo y los combina en un solo valor.
$reducetiene la siguiente sintaxis:{ $reduce: { input: <array>, initialValue: <expression>, in: <expression>, as: <string>, valueAs: <string>, arrayIndexAs: <string> } } CampoTipoDescripcióninputarreglo
Puede ser cualquier valor válido expresión que resuelve a un arreglo. Para obtener más información sobre expresiones, consulta Expresiones.
Si el argumento se resuelve en un valor de
nullo se refiere a un campo que falta,$reducedevuelvenull.Si el argumento no se resuelve en un arreglo o
null, ni hace referencia a un campo ausente,$reducedevuelve un error.initialValueexpresión
El
valueacumulativo inicial establecido antes de queinse aplique al primer elemento del arregloinput.inexpresión
Una expresión válida que
$reducese aplica a cada elemento del arregloinputen orden de izquierda a derecha. Envuelve el valorinputcon$reverseArraypara obtener el equivalente de aplicar la expresión de combinación de derecha a izquierda.Durante la evaluación de la expresión
in, habrá dos variables disponibles:asstring
opcional. Un nombre para la variable que representa cada elemento individual del arreglo
input. Si se omite, el nombre de la variable seráthispor defecto.Nuevo en la versión 8.3.
valueAsstring
opcional. Un nombre para la variable que representa el valor acumulativo de la expresión. Si se omite, el nombre de la variable será
valuepor defecto.Nuevo en la versión 8.3.
arrayIndexAsstring
opcional. Un nombre para la variable de agregación que representa el índice del elemento actual en el arreglo
input. El índice del primer elemento del arreglo es0.Puede usar el nombre de la variable en una expresión. Por ejemplo, si se especifica
arrayIndexAs: "myIndex", se usa$$myIndexen la expresión.$$myIndexdevuelve el índice del elemento actual en el arregloinput.Si omites
arrayIndexAs, puedes utilizar la variable del sistema$$IDXen la expresión para devolver el índice del elemento actual.Para ejemplos, consulta Accede al índice de cada elemento en un arreglo y Utiliza
$$IDXpara acceder al índice.Nuevo en la versión 8.3.
Si
inputse resuelve en un arreglo vacío,$reducedevuelveinitialValue.
Ejemplo | Resultados | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
| ||||||||||
|
| ||||||||||
|
|
Ejemplos
Multiplicación
Probabilidad
Una colección denominada events contiene los eventos de un experimento de probabilidad. Cada experimento puede tener varios events, como lanzar un dado varias veces o robar cartas (sin reemplazo) seguidas para lograr un resultado deseado. Para obtener la probabilidad general del experimento, necesitaremos multiplicar la probabilidad de cada evento en el experimento.
db.events.insertMany( [ { _id : 1, type : "die", experimentId :"r5", description : "Roll a 5", eventNum : 1, probability : 0.16666666666667 }, { _id : 2, type : "card", experimentId :"d3rc", description : "Draw 3 red cards", eventNum : 1, probability : 0.5 }, { _id : 3, type : "card", experimentId :"d3rc", description : "Draw 3 red cards", eventNum : 2, probability : 0.49019607843137 }, { _id : 4, type : "card", experimentId :"d3rc", description : "Draw 3 red cards", eventNum : 3, probability : 0.48 }, { _id : 5, type : "die", experimentId :"r16", description : "Roll a 1 then a 6", eventNum : 1, probability : 0.16666666666667 }, { _id : 6, type : "die", experimentId :"r16", description : "Roll a 1 then a 6", eventNum : 2, probability : 0.16666666666667 }, { _id : 7, type : "card", experimentId :"dak", description : "Draw an ace, then a king", eventNum : 1, probability : 0.07692307692308 }, { _id : 8, type : "card", experimentId :"dak", description : "Draw an ace, then a king", eventNum : 2, probability : 0.07843137254902 } ] )
Pasos:
Usa
$grouppara agrupar porexperimentIdy usa$pushpara crear un arreglo con la probabilidad de cada evento.Usa
$reducecon$multiplypara multiplicar y combinar los elementos deprobabilityArren un solo valor y proyéctalo.
db.probability.aggregate( [ { $group: { _id: "$experimentId", probabilityArr: { $push: "$probability" } } }, { $project: { description: 1, results: { $reduce: { input: "$probabilityArr", initialValue: 1, in: { $multiply: [ "$$value", "$$this" ] } } } } } ] )
La operación devuelve lo siguiente:
{ _id : "dak", results : 0.00603318250377101 } { _id : "r5", results : 0.16666666666667 } { _id : "r16", results : 0.027777777777778886 } { _id : "d3rc", results : 0.11764705882352879 }
Mercancía con descuento
Una colección llamada clothes contiene los siguientes documentos:
db.clothes.insertMany( [ { _id : 1, productId : "ts1", description : "T-Shirt", color : "black", size : "M", price : 20, discounts : [ 0.5, 0.1 ] }, { _id : 2, productId : "j1", description : "Jeans", color : "blue", size : "36", price : 40, discounts : [ 0.25, 0.15, 0.05 ] }, { _id : 3, productId : "s1", description : "Shorts", color : "beige", size : "32", price : 30, discounts : [ 0.15, 0.05 ] }, { _id : 4, productId : "ts2", description : "Cool T-Shirt", color : "White", size : "L", price : 25, discounts : [ 0.3 ] }, { _id : 5, productId : "j2", description : "Designer Jeans", color : "blue", size : "30", price : 80, discounts : [ 0.1, 0.25 ] } ] )
Cada documento contiene un arreglo discounts con los cupones de descuento disponibles actualmente para cada artículo. Si cada descuento puede aplicarse al producto una vez, podemos calcular el precio más bajo mediante $reduce para aplicar la siguiente fórmula a cada elemento del arreglo discounts: (1 - descuento) * precio.
db.clothes.aggregate( [ { $project: { discountedPrice: { $reduce: { input: "$discounts", initialValue: "$price", in: { $multiply: [ "$$value", { $subtract: [ 1, "$$this" ] } ] } } } } } ] )
La operación devuelve lo siguiente:
{ _id : ObjectId("57c893067054e6e47674ce01"), discountedPrice : 9 } { _id : ObjectId("57c9932b7054e6e47674ce12"), discountedPrice : 24.224999999999998 } { _id : ObjectId("57c993457054e6e47674ce13"), discountedPrice : 24.224999999999998 } { _id : ObjectId("57c993687054e6e47674ce14"), discountedPrice : 17.5 } { _id : ObjectId("57c993837054e6e47674ce15"), discountedPrice : 54 }
Concatenación de string
Una colección llamada people contiene los siguientes documentos:
db.people.insertMany( [ { _id : 1, name : "Melissa", hobbies : [ "softball", "drawing", "reading" ] }, { _id : 2, name : "Brad", hobbies : [ "gaming", "skateboarding" ] }, { _id : 3, name : "Scott", hobbies : [ "basketball", "music", "fishing" ] }, { _id : 4, name : "Tracey", hobbies : [ "acting", "yoga" ] }, { _id : 5, name : "Josh", hobbies : [ "programming" ] }, { _id : 6, name : "Claire" } ] )
En el siguiente ejemplo, se reduce el arreglo hobbies de strings en un solo string bio:
db.people.aggregate( [ // Filter to return only non-empty arrays { $match: { "hobbies": { $gt: [ ] } } }, { $project: { name: 1, bio: { $reduce: { input: "$hobbies", initialValue: "My hobbies include:", in: { $concat: [ "$$value", { $cond: { if: { $eq: [ "$$value", "My hobbies include:" ] }, then: " ", else: ", " } }, "$$this" ] } } } } } ] )
La operación devuelve lo siguiente:
{ _id : 1, name : "Melissa", bio : "My hobbies include: softball, drawing, reading" } { _id : 2, name : "Brad", bio : "My hobbies include: gaming, skateboarding" } { _id : 3, name : "Scott", bio : "My hobbies include: basketball, music, fishing" } { _id : 4, name : "Tracey", bio : "My hobbies include: acting, yoga" } { _id : 5, name : "Josh", bio : "My hobbies include: programming" }
Concatenación de arreglos
Una colección llamada matrices contiene los siguientes documentos:
db.matrices.insertMany( [ { _id : 1, arr : [ [ 24, 55, 79 ], [ 14, 78, 35 ], [ 84, 90, 3 ], [ 50, 89, 70 ] ] }, { _id : 2, arr : [ [ 39, 32, 43, 7 ], [ 62, 17, 80, 64 ], [ 17, 88, 11, 73 ] ] }, { _id : 3, arr : [ [ 42 ], [ 26, 59 ], [ 17 ], [ 72, 19, 35 ] ] }, { _id : 4 } ] )
Cálculo de una única reducción
En el siguiente ejemplo, se contraen los arreglos bidimensionales en un solo arreglo collapsed:
db.arrayconcat.aggregate( [ { $project: { collapsed: { $reduce: { input: "$arr", initialValue: [ ], in: { $concatArrays: [ "$$value", "$$this" ] } } } } } ] )
La operación devuelve lo siguiente:
{ _id : 1, collapsed : [ 24, 55, 79, 14, 78, 35, 84, 90, 3, 50, 89, 70 ] } { _id : 2, collapsed : [ 39, 32, 43, 7, 62, 17, 80, 64, 17, 88, 11, 73 ] } { _id : 3, collapsed : [ 42, 26, 59, 17, 72, 19, 35 ] } { _id : 4, collapsed : null }
Cálculo de varias reducciones
En el siguiente ejemplo, se realiza la misma contracción del arreglo bidimensional que en el ejemplo anterior, pero también se crea un nuevo arreglo que contiene solo el primer elemento de cada arreglo.
db.arrayconcat.aggregate( [ { $project: { results: { $reduce: { input: "$arr", initialValue: [ ], in: { collapsed: { $concatArrays: [ "$$value.collapsed", "$$this" ] }, firstValues: { $concatArrays: [ "$$value.firstValues", { $slice: [ "$$this", 1 ] } ] } } } } } } ] )
La operación devuelve lo siguiente:
{ _id : 1, results : { collapsed : [ 24, 55, 79, 14, 78, 35, 84, 90, 3, 50, 89, 70 ], firstValues : [ 24, 14, 84, 50 ] } } { _id : 2, results : { collapsed : [ 39, 32, 43, 7, 62, 17, 80, 64, 17, 88, 11, 73 ], firstValues : [ 39, 62, 17 ] } } { _id : 3, results : { collapsed : [ 42, 26, 59, 17, 72, 19, 35 ], firstValues : [ 42, 26, 17, 72 ] } } { _id : 4, results : null }
Accede al índice de cada elemento de un arreglo
Crea una colección de muestra llamada people con estos document:
db.people.insertMany( [ { _id: 1, name: "Melissa", hobbies: [ "softball", "drawing", "reading" ] }, { _id: 2, name: "Brad", hobbies: [ "gaming", "skateboarding" ] }, { _id: 3, name: "Scott", hobbies: [ "basketball", "music", "fishing" ] }, { _id: 4, name: "Tracey", hobbies: [ "acting", "yoga" ] }, { _id: 5, name: "Josh", hobbies: [ "programming" ] }, { _id: 6, name: "Claire" } ] )
El campo hobbies contiene un arreglo con los pasatiempos de cada persona en orden de clasificación. El primer hobby en el arreglo es el hobby principal de la persona al que le dedica más tiempo. El primer pasatiempo tiene un índice de arreglo de 0.
El siguiente ejemplo utiliza valueAs, as y arrayIndexAs. La variable myIndex tiene el índice de cada pasatiempo en el arreglo hobbies. El ejemplo devuelve documentos con estos campos:
Nombre de la persona.
textcampo, que incluye el nombre del pasatiempo y la posición de cada pasatiempo en la lista.
db.people.aggregate( [ { $project: { _id: 0, name: 1, text: { $reduce: { input: "$hobbies", initialValue: "My hobbies include:", valueAs: "accumulatedText", as: "hobby", arrayIndexAs: "myIndex", in: { $concat: [ "$$accumulatedText", " ", { $toString: { $add: [ "$$myIndex", 1 ] } }, ") ", "$$hobby" ] } } } } } ] )
Salida:
[ { "name" : "Melissa", "text" : "My hobbies include: 1) softball 2) drawing 3) reading" } { "name" : "Brad", "text" : "My hobbies include: 1) gaming 2) skateboarding" } { "name" : "Scott", "text" : "My hobbies include: 1) basketball 2) music 3) fishing" } { "name" : "Tracey", "text" : "My hobbies include: 1) acting 2) yoga" } { "name" : "Josh", "text" : "My hobbies include: 1) programming" } { "name" : "Claire", "text" : null } ]
Use $$IDX para acceder al índice
La $$IDX variable devuelve el índice del elemento actual en el arreglo input. Puede usar $$IDX si omite el campo arrayIndexAs de la expresión.
El siguiente ejemplo devuelve los mismos **documents** que el ejemplo de la sección **anterior** Acceder al **índice** de cada elemento de un **arreglo**, pero utiliza $$IDX en lugar de arrayIndexAs:
db.people.aggregate( [ { $project: { _id: 0, name: 1, text: { $reduce: { input: "$hobbies", initialValue: "My hobbies include:", valueAs: "accumulatedText", as: "hobby", in: { $concat: [ "$$accumulatedText", " ", { $toString: { $add: [ "$$IDX", 1 ] } }, ") ", "$$hobby" ] } } } } } ] )