skip to Main Content

I want to slice array in document according to slicingIndex key available in the same document and push into an array.

[
  {
    slicingIndex: [0, 4, 7, 10],
    array: [
      { name: 1 },
      { name: 2 },
      { name: 3 },
      { name: 4 },
      { name: 5 },
      { name: 6 },
      { name: 7 },
      { name: 8 },
      { name: 9 },
      { name: 10 }
    ]
  }
]

Output (expected):

{
  "array": [
    [
      { "name": 1 },
      { "name": 2 },
      { "name": 3 },
      { "name": 4 }
    ],
    [
      { "name": 5 },
      { "name": 6 },
      { "name": 7 }
    ],
    [
      { "name": 8 },
      { "name": 9 },
      { "name": 10 }
    ]
  ]
}

This is what aggregation i tried below but not working as expected:

db.collection.aggregate([
  {
    "$project": {
      slicingIndex: 1,
      array: {
        "$map": {
          "input": "$slicingIndex",
          "as": "num",
          "in": {
            "$slice": [
              "$array",
              "$$num",
              {
                "$subtract": [
                  {
                    "$arrayElemAt": ["$slicingIndex", { "$indexOfArray": ["$slicingIndex", "$$num"] }]
                  },
                  "$$num"
                ]
              }
            ]
          }
        }
      }
    }
  }
])

2

Answers


    1. Create two arrays for the pairs of the indexes for the slices, by zipping slicingIndex with itself. aka "zip with next".
      For the start index – take n elements where n = size minus 1. And for the end index, start from the end using minus n for slice.

      • this gives the arrays startIndexes: [0, 4, 7] and endIndexes: [4, 7, 10]
      • edit: Changed multiply { subtract size 1 }, -1 to just subtract 1 size since (n - 1) * -1 == 1 - n
    2. Then zip these two together to get Start & End index pairs.

      • sliceIdxPairs is [[0, 4], [4, 7], [7, 10]]
    3. But since the params for $slice aggregation are position (start index) and n (number of elements), what we need is to subtract the endIndex from the startIndex in each of the pairs.

      • sliceParams is [[0, 4], [4, 3], [7, 3]]
    4. Finally, $map the pairs of numbers in the sliceParams and use them with $slice on the array field.

    db.collection.aggregate([
      {
        $project: {
          array: 1,
          startIndexes: {
            $slice: [
              "$slicingIndex",
              { $subtract: [{ $size: "$slicingIndex" }, 1] }
            ]
          },
          endIndexes: {
            $slice: [
              "$slicingIndex",
              { $subtract: [1, { $size: "$slicingIndex" }] }
            ]
          }
        }
      },
      {
        $project: {
          array: 1,
          sliceIdxPairs: {
            $zip: { inputs: ["$startIndexes", "$endIndexes"] }
          }
        }
      },
      {
        $project: {
          array: 1,
          sliceParams: {
            $map: {
              input: "$sliceIdxPairs",
              as: "ip",
              in: [
                { $first: "$$ip" },
                { $subtract: [{ $last: "$$ip" }, { $first: "$$ip" }] }
              ]
            }
          }
        }
      },
      {
        $project: {
          array: {
            $map: {
              input: "$sliceParams",
              as: "pair",
              in: {
                $slice: [
                  "$array",
                  { $first: "$$pair" },
                  { $last: "$$pair" }
                ]
              }
            }
          }
        }
      }
    ])
    

    Mongo Playground

    Login or Signup to reply.
  1. Tricky question!

    You should start the iteration from the second element of slicingIndex. To slice the array, you need to get the ($slice) start index with the value of the previous element of the current iterated element (current index – 1) and the n should be current element – previous element.

    db.collection.aggregate([
      {
        "$project": {
          slicingIndex: 1,
          array: {
            "$map": {
              "input": {
                $slice: [
                  "$slicingIndex",
                  1,
                  {
                    $size: "$slicingIndex"
                  }
                ]
              },
              "as": "num",
              "in": {
                "$slice": [
                  "$array",
                  {
                    "$arrayElemAt": [
                      "$slicingIndex",
                      {
                        $subtract: [
                          {
                            "$indexOfArray": [
                              "$slicingIndex",
                              "$$num"
                            ]
                          },
                          1
                        ]
                      }
                    ]
                  },
                  {
                    $subtract: [
                      "$$num",
                      {
                        "$arrayElemAt": [
                          "$slicingIndex",
                          {
                            $subtract: [
                              {
                                "$indexOfArray": [
                                  "$slicingIndex",
                                  "$$num"
                                ]
                              },
                              1
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            }
          }
        }
      }
    ])
    

    Demo @ Mongo Playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search