$zip (aggregation)
Definition
New in version 3.4.
Transposes an array of input arrays so that the first element ofthe output array would be an array containing, the first element ofthe first input array, the first element of the second input array,etc.
For example, $zip
would transform[ [ 1, 2, 3 ], [ "a", "b", "c" ] ]
into[ [ 1, "a" ], [ 2, "b" ], [ 3, "c" ] ]
.
$zip
has the following syntax:
- {
- $zip: {
- inputs: [ <array expression1>, ... ],
- useLongestLength: <boolean>,
- defaults: <array expression>
- }
- }
OperandDescriptioninputs
An array of expressions thatresolve to arrays. The elements of these input arrays combineto form the arrays of the output array.
If any of the inputs
arrays resolves to a value of null
or refers to amissing field, $zip
returns null
.
If any of the inputs
arrays does not resolve to an array or null
nor refersto a missing field, $zip
returns an error.useLongestLength
A boolean which specifies whether the length of the longestarray determines the number of arrays in the output array.
The default value is false
: the shortest array lengthdetermines the number of arrays in the output array.defaults
An array of default element values to use if the input arrayshave different lengths. You must specifyuseLongestLength: true
along with this field, or else$zip
will return an error.
If useLongestLength: true
but defaults
is empty or notspecified, $zip
uses null
as the defaultvalue.
If specifying a non-empty defaults
, you must specify adefault for each input array or else $zip
will return an error.
Behavior
The input arrays do not need to be of the same length. By default,the output array has the length of the shortest input array, but theuseLongestLength
option instructs $zip
to outputan array as long as the longest input array.
Example | Results |
---|---|
|
|
|
|
|
|
| Because useLongestLength: true , $zip will pad the shorterinput arrays with the corresponding defaults elements.This yields [ [ 1, 2, 4 ], [ "a", 3, "c" ] ] . |
Example
Matrix Transposition
A collection called matrices
contains the following documents:
- db.matrices.insertMany([
- { matrix: [[1, 2], [2, 3], [3, 4]] },
- { matrix: [[8, 7], [7, 6], [5, 4]] },
- ])
To compute the transpose of each 3x2 matrix in this collection, you canuse the following aggregation operation:
- db.matrices.aggregate([{
- $project: {
- _id: false,
- transposed: {
- $zip: {
- inputs: [
- { $arrayElemAt: [ "$matrix", 0 ] },
- { $arrayElemAt: [ "$matrix", 1 ] },
- { $arrayElemAt: [ "$matrix", 2 ] },
- ]
- }
- }
- }
- }])
This will return the following 2x3 matrices:
- { "transposed" : [ [ 1, 2, 3 ], [ 2, 3, 4 ] ] }
- { "transposed" : [ [ 8, 7, 5 ], [ 7, 6, 4 ] ] }
Filtering and Preserving Indexes
You can use $zip
with $filter
to obtain a subset ofelements in an array, saving the original index alongside eachretained element.
A collection called pages
contains the following document:
- db.pages.save( {
- "category": "unix",
- "pages": [
- { "title": "awk for beginners", reviews: 5 },
- { "title": "sed for newbies", reviews: 0 },
- { "title": "grep made simple", reviews: 2 },
- ] } )
The following aggregation pipeline will first zip the elements of thepages
array together with their index, and then filter out only thepages with at least one review:
- db.pages.aggregate([{
- $project: {
- _id: false,
- pages: {
- $filter: {
- input: {
- $zip: {
- inputs: [ "$pages", { $range: [0, { $size: "$pages" }] } ]
- }
- },
- as: "pageWithIndex",
- cond: {
- $let: {
- vars: {
- page: { $arrayElemAt: [ "$$pageWithIndex", 0 ] }
- },
- in: { $gte: [ "$$page.reviews", 1 ] }
- }
- }
- }
- }
- }
- }])
This will return the following document:
- {
- "pages" : [
- [ { "title" : "awk for beginners", "reviews" : 5 }, 0 ],
- [ { "title" : "grep made simple", "reviews" : 2 }, 2 ] ]
- }