Efficient way of flat mapping a range of a multidimensional random access collectionPartitioning array...

Why is this code 6.5x slower with optimizations enabled?

Are white and non-white police officers equally likely to kill black suspects?

New order #4: World

How can bays and straits be determined in a procedurally generated map?

Can Medicine checks be used, with decent rolls, to completely mitigate the risk of death from ongoing damage?

Is there a minimum number of transactions in a block?

DOS, create pipe for stdin/stdout of command.com(or 4dos.com) in C or Batch?

How to report a triplet of septets in NMR tabulation?

What would happen to a modern skyscraper if it rains micro blackholes?

How is the claim "I am in New York only if I am in America" the same as "If I am in New York, then I am in America?

How does one intimidate enemies without having the capacity for violence?

I’m planning on buying a laser printer but concerned about the life cycle of toner in the machine

I probably found a bug with the sudo apt install function

What makes Graph invariants so useful/important?

Schwarzchild Radius of the Universe

How to calculate implied correlation via observed market price (Margrabe option)

What defenses are there against being summoned by the Gate spell?

Is it possible to make sharp wind that can cut stuff from afar?

Why did the Germans forbid the possession of pet pigeons in Rostov-on-Don in 1941?

What Brexit solution does the DUP want?

declaring a variable twice in IIFE

least quadratic residue under GRH: an EXPLICIT bound

Is it possible to do 50 km distance without any previous training?

Can I make popcorn with any corn?



Efficient way of flat mapping a range of a multidimensional random access collection


Partitioning array elements into two subsetsBuilding a histogram array in PHPCount the accumulated rainfall in a 2D mountain rangeUsing 2D arrays to make more efficient sequential storageFind the max. difference between two array elements a[j] and a[i] such that j > iFinding an equilibrium index in an int arrayCalculating sum after ranged-based subtractionsprint array of previous smaller elementsShuffling an array keeping some elements fixedUse arrays to perform 'VLookUp' type activity between workbooks






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}







3












$begingroup$


I've recently answered a question about reading elements from an array of arrays. A way it could be interpreted is that the OP wanted to read a range that could span over multiple subarrays of the 2D array like so :



let a = [["5", "3", ".", ".", "7", "."],
["6", ".", ".", "1", "9", "5"]]

a.lazy.flatMap{$0}[1..<7] ["3", ".", ".", "7", ".", "6"]


This way of reading a range would need to at least flatMap all the previous arrays to the lower bound of the range. Which would be wasteful.



A more natural way would be to only read the needed elements from the original array:



func get<T>(_ range: Range<Int>, from array2d: [[T]]) -> [T]? {
var result: [T] = []
result.reserveCapacity(range.upperBound - range.lowerBound)

var count1 = 0

//Get the index of the first element
guard var low = array2d
.firstIndex(where: {
count1 += $0.count
return range.lowerBound < count1 }),
count1 != 0
else { return nil }

let before = count1 - array2d[low].count
var count2 = before

//Get the index of the last element in the range
guard let high = array2d[low..<array2d.endIndex]
.firstIndex(where: {
count2 += $0.count
return range.upperBound <= count2
}),
count2 != 0
else { return nil }

//Append the elements in the array with the low index
for i in (range.lowerBound - before)..<min(range.upperBound - before, array2d[low].count) {
result.append(array2d[low][i])
}

//If the range spans over multiple arrays
if count1 < count2 {
low += 1

//Append the elements in the arrays with an index between low and high
while low < high {
result.append(contentsOf: array2d[low])
low += 1
}

//Append the elements in the array with the high index
for i in 0..<(range.upperBound - count2 + array2d[high].count) {
result.append(array2d[high][i])
}
}

return result
}


Which could be used like so :



let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]

get(5..<11, from: a) //Optional(["5", "6", "7", "8", "9", "10"])
get(7..<9, from: a) //Optional(["7", "8"])


To me, the above code feels.. on the border of sanity/maintainability...



What I'd like to do is to make it more generic, maybe as an extension to RandomAccessCollection, and making the flattening process recursive for arrays/collections of arbitrary dimensions. I'm stuck here and not sure if this is the appropriate network to ask such a question.



Feedback on all aspects of the code is welcome, such as (but not limited to):




  • Efficiency,

  • Readability,

  • Naming.










share|improve this question











$endgroup$



















    3












    $begingroup$


    I've recently answered a question about reading elements from an array of arrays. A way it could be interpreted is that the OP wanted to read a range that could span over multiple subarrays of the 2D array like so :



    let a = [["5", "3", ".", ".", "7", "."],
    ["6", ".", ".", "1", "9", "5"]]

    a.lazy.flatMap{$0}[1..<7] ["3", ".", ".", "7", ".", "6"]


    This way of reading a range would need to at least flatMap all the previous arrays to the lower bound of the range. Which would be wasteful.



    A more natural way would be to only read the needed elements from the original array:



    func get<T>(_ range: Range<Int>, from array2d: [[T]]) -> [T]? {
    var result: [T] = []
    result.reserveCapacity(range.upperBound - range.lowerBound)

    var count1 = 0

    //Get the index of the first element
    guard var low = array2d
    .firstIndex(where: {
    count1 += $0.count
    return range.lowerBound < count1 }),
    count1 != 0
    else { return nil }

    let before = count1 - array2d[low].count
    var count2 = before

    //Get the index of the last element in the range
    guard let high = array2d[low..<array2d.endIndex]
    .firstIndex(where: {
    count2 += $0.count
    return range.upperBound <= count2
    }),
    count2 != 0
    else { return nil }

    //Append the elements in the array with the low index
    for i in (range.lowerBound - before)..<min(range.upperBound - before, array2d[low].count) {
    result.append(array2d[low][i])
    }

    //If the range spans over multiple arrays
    if count1 < count2 {
    low += 1

    //Append the elements in the arrays with an index between low and high
    while low < high {
    result.append(contentsOf: array2d[low])
    low += 1
    }

    //Append the elements in the array with the high index
    for i in 0..<(range.upperBound - count2 + array2d[high].count) {
    result.append(array2d[high][i])
    }
    }

    return result
    }


    Which could be used like so :



    let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]

    get(5..<11, from: a) //Optional(["5", "6", "7", "8", "9", "10"])
    get(7..<9, from: a) //Optional(["7", "8"])


    To me, the above code feels.. on the border of sanity/maintainability...



    What I'd like to do is to make it more generic, maybe as an extension to RandomAccessCollection, and making the flattening process recursive for arrays/collections of arbitrary dimensions. I'm stuck here and not sure if this is the appropriate network to ask such a question.



    Feedback on all aspects of the code is welcome, such as (but not limited to):




    • Efficiency,

    • Readability,

    • Naming.










    share|improve this question











    $endgroup$















      3












      3








      3


      1



      $begingroup$


      I've recently answered a question about reading elements from an array of arrays. A way it could be interpreted is that the OP wanted to read a range that could span over multiple subarrays of the 2D array like so :



      let a = [["5", "3", ".", ".", "7", "."],
      ["6", ".", ".", "1", "9", "5"]]

      a.lazy.flatMap{$0}[1..<7] ["3", ".", ".", "7", ".", "6"]


      This way of reading a range would need to at least flatMap all the previous arrays to the lower bound of the range. Which would be wasteful.



      A more natural way would be to only read the needed elements from the original array:



      func get<T>(_ range: Range<Int>, from array2d: [[T]]) -> [T]? {
      var result: [T] = []
      result.reserveCapacity(range.upperBound - range.lowerBound)

      var count1 = 0

      //Get the index of the first element
      guard var low = array2d
      .firstIndex(where: {
      count1 += $0.count
      return range.lowerBound < count1 }),
      count1 != 0
      else { return nil }

      let before = count1 - array2d[low].count
      var count2 = before

      //Get the index of the last element in the range
      guard let high = array2d[low..<array2d.endIndex]
      .firstIndex(where: {
      count2 += $0.count
      return range.upperBound <= count2
      }),
      count2 != 0
      else { return nil }

      //Append the elements in the array with the low index
      for i in (range.lowerBound - before)..<min(range.upperBound - before, array2d[low].count) {
      result.append(array2d[low][i])
      }

      //If the range spans over multiple arrays
      if count1 < count2 {
      low += 1

      //Append the elements in the arrays with an index between low and high
      while low < high {
      result.append(contentsOf: array2d[low])
      low += 1
      }

      //Append the elements in the array with the high index
      for i in 0..<(range.upperBound - count2 + array2d[high].count) {
      result.append(array2d[high][i])
      }
      }

      return result
      }


      Which could be used like so :



      let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]

      get(5..<11, from: a) //Optional(["5", "6", "7", "8", "9", "10"])
      get(7..<9, from: a) //Optional(["7", "8"])


      To me, the above code feels.. on the border of sanity/maintainability...



      What I'd like to do is to make it more generic, maybe as an extension to RandomAccessCollection, and making the flattening process recursive for arrays/collections of arbitrary dimensions. I'm stuck here and not sure if this is the appropriate network to ask such a question.



      Feedback on all aspects of the code is welcome, such as (but not limited to):




      • Efficiency,

      • Readability,

      • Naming.










      share|improve this question











      $endgroup$




      I've recently answered a question about reading elements from an array of arrays. A way it could be interpreted is that the OP wanted to read a range that could span over multiple subarrays of the 2D array like so :



      let a = [["5", "3", ".", ".", "7", "."],
      ["6", ".", ".", "1", "9", "5"]]

      a.lazy.flatMap{$0}[1..<7] ["3", ".", ".", "7", ".", "6"]


      This way of reading a range would need to at least flatMap all the previous arrays to the lower bound of the range. Which would be wasteful.



      A more natural way would be to only read the needed elements from the original array:



      func get<T>(_ range: Range<Int>, from array2d: [[T]]) -> [T]? {
      var result: [T] = []
      result.reserveCapacity(range.upperBound - range.lowerBound)

      var count1 = 0

      //Get the index of the first element
      guard var low = array2d
      .firstIndex(where: {
      count1 += $0.count
      return range.lowerBound < count1 }),
      count1 != 0
      else { return nil }

      let before = count1 - array2d[low].count
      var count2 = before

      //Get the index of the last element in the range
      guard let high = array2d[low..<array2d.endIndex]
      .firstIndex(where: {
      count2 += $0.count
      return range.upperBound <= count2
      }),
      count2 != 0
      else { return nil }

      //Append the elements in the array with the low index
      for i in (range.lowerBound - before)..<min(range.upperBound - before, array2d[low].count) {
      result.append(array2d[low][i])
      }

      //If the range spans over multiple arrays
      if count1 < count2 {
      low += 1

      //Append the elements in the arrays with an index between low and high
      while low < high {
      result.append(contentsOf: array2d[low])
      low += 1
      }

      //Append the elements in the array with the high index
      for i in 0..<(range.upperBound - count2 + array2d[high].count) {
      result.append(array2d[high][i])
      }
      }

      return result
      }


      Which could be used like so :



      let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]

      get(5..<11, from: a) //Optional(["5", "6", "7", "8", "9", "10"])
      get(7..<9, from: a) //Optional(["7", "8"])


      To me, the above code feels.. on the border of sanity/maintainability...



      What I'd like to do is to make it more generic, maybe as an extension to RandomAccessCollection, and making the flattening process recursive for arrays/collections of arbitrary dimensions. I'm stuck here and not sure if this is the appropriate network to ask such a question.



      Feedback on all aspects of the code is welcome, such as (but not limited to):




      • Efficiency,

      • Readability,

      • Naming.







      performance array swift collections






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Apr 3 at 9:29







      ielyamani

















      asked Apr 1 at 13:47









      ielyamaniielyamani

      372213




      372213






















          2 Answers
          2






          active

          oldest

          votes


















          1












          $begingroup$

          I gather that your intent was to write a method that iterates through an array of arrays (but avoiding functional patterns), flattening the results in the process such that given...



          let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]


          ... that result for 5..<10 would be ["5", "6", "7", "8", "9"]



          Assuming that’s what you were trying to do, I think you can simplify it:



          extension Array {
          func flattened<T>(range: Range<Int>) -> [T]? where Element == Array<T> {
          var result: [T] = []

          var offset = range.startIndex
          var length = range.upperBound - range.lowerBound

          result.reserveCapacity(length)

          for subarray in self {
          let subarrayCount = subarray.count
          if offset < subarrayCount {
          if length > subarrayCount - offset {
          result += subarray[offset...]
          length -= subarrayCount - offset
          } else {
          return result + subarray[offset..<offset + length]
          }
          }
          offset = Swift.max(0, offset - subarrayCount)
          }

          return nil
          }
          }


          In terms of observations on your code:




          • I wouldn’t advise get method name. It’s not very meaningful name and only conjures up confusion with getters. I’d go with something that captured the “flattening” nature of the routine.


          • As a general rule, we should avoid closures with side-effects in functional programming patterns. Even though you’ve written an “iterative” rendition of the OP’s code, you’re using a functional method, firstIndex, and you are updating variables outside the closure. It’s technically allowed but is contrary to the spirit of functional programming patterns and is dependent upon the implementation details of firstIndex.







          share|improve this answer









          $endgroup$













          • $begingroup$
            FYI, the above is a simple refactoring of the code snippet in the question, but does not attempt to tackle the question of what changes are needed to handle arbitrary depth in the subarrays.
            $endgroup$
            – Rob
            Apr 3 at 8:40



















          0












          $begingroup$

          Extensibility



          The following is meant as an addendum to Rob's answer. It is built upon their approach and extends it to work for arrays with arbitrary depth.



          extension Array {
          func flattened(range: Range<Int>) -> [Any]? {
          return helper(range).result?.count == range.upperBound - range.lowerBound ?
          helper(range).result :
          nil
          }

          private func helper(_ range: Range<Int>) -> (result: [Any]?, offset: Int) {
          var result: [Any] = []

          var offset = range.startIndex
          let length = range.upperBound - range.lowerBound

          result.reserveCapacity(length)

          for i in self.indices {
          let element = self[i]

          if let sub_a: [Any] = element as? [Any] {
          let tempo = sub_a.helper(offset..<offset + length - result.count)
          if let res = tempo.result {
          result += res
          offset = tempo.offset
          } else {
          return (result: nil, offset: offset)
          }
          } else {
          if offset == 0 {
          result.append(element)
          }
          offset = Swift.max(0, offset - 1)
          }

          if result.count == length {
          return (result: result, offset: offset)
          }
          }

          return (result: result, offset: offset)
          }
          }


          It has been tested with all of these arrays :



          let a: [Any] = [[["0", "1", "2",   "3",  "4",    "5"], ["6",  "7"], []],
          [["8", "9", "10", "11", "12"], ["13", "14", "15"]]]

          let b: [Any] = [[["0", "1"], ["2", "3", "4"]],
          [[]],
          [["5"], ["6", "7", "8", "9"]],
          [["10", "11", "12"], ["13", "14", "15"]]]

          let c: [Any] = [["0", "1", "2", "3", "4", "5", ["6", "7", ["8", "9", "10", "11", "12"]]], [], ["13", "14", "15"]]


          The output of all three :



          print(a.flattened(range: 3..<15))
          print(b.flattened(range: 3..<15))
          print(c.flattened(range: 3..<15))


          is




          Optional(["3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14"])







          share|improve this answer









          $endgroup$














            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216657%2fefficient-way-of-flat-mapping-a-range-of-a-multidimensional-random-access-collec%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            1












            $begingroup$

            I gather that your intent was to write a method that iterates through an array of arrays (but avoiding functional patterns), flattening the results in the process such that given...



            let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]


            ... that result for 5..<10 would be ["5", "6", "7", "8", "9"]



            Assuming that’s what you were trying to do, I think you can simplify it:



            extension Array {
            func flattened<T>(range: Range<Int>) -> [T]? where Element == Array<T> {
            var result: [T] = []

            var offset = range.startIndex
            var length = range.upperBound - range.lowerBound

            result.reserveCapacity(length)

            for subarray in self {
            let subarrayCount = subarray.count
            if offset < subarrayCount {
            if length > subarrayCount - offset {
            result += subarray[offset...]
            length -= subarrayCount - offset
            } else {
            return result + subarray[offset..<offset + length]
            }
            }
            offset = Swift.max(0, offset - subarrayCount)
            }

            return nil
            }
            }


            In terms of observations on your code:




            • I wouldn’t advise get method name. It’s not very meaningful name and only conjures up confusion with getters. I’d go with something that captured the “flattening” nature of the routine.


            • As a general rule, we should avoid closures with side-effects in functional programming patterns. Even though you’ve written an “iterative” rendition of the OP’s code, you’re using a functional method, firstIndex, and you are updating variables outside the closure. It’s technically allowed but is contrary to the spirit of functional programming patterns and is dependent upon the implementation details of firstIndex.







            share|improve this answer









            $endgroup$













            • $begingroup$
              FYI, the above is a simple refactoring of the code snippet in the question, but does not attempt to tackle the question of what changes are needed to handle arbitrary depth in the subarrays.
              $endgroup$
              – Rob
              Apr 3 at 8:40
















            1












            $begingroup$

            I gather that your intent was to write a method that iterates through an array of arrays (but avoiding functional patterns), flattening the results in the process such that given...



            let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]


            ... that result for 5..<10 would be ["5", "6", "7", "8", "9"]



            Assuming that’s what you were trying to do, I think you can simplify it:



            extension Array {
            func flattened<T>(range: Range<Int>) -> [T]? where Element == Array<T> {
            var result: [T] = []

            var offset = range.startIndex
            var length = range.upperBound - range.lowerBound

            result.reserveCapacity(length)

            for subarray in self {
            let subarrayCount = subarray.count
            if offset < subarrayCount {
            if length > subarrayCount - offset {
            result += subarray[offset...]
            length -= subarrayCount - offset
            } else {
            return result + subarray[offset..<offset + length]
            }
            }
            offset = Swift.max(0, offset - subarrayCount)
            }

            return nil
            }
            }


            In terms of observations on your code:




            • I wouldn’t advise get method name. It’s not very meaningful name and only conjures up confusion with getters. I’d go with something that captured the “flattening” nature of the routine.


            • As a general rule, we should avoid closures with side-effects in functional programming patterns. Even though you’ve written an “iterative” rendition of the OP’s code, you’re using a functional method, firstIndex, and you are updating variables outside the closure. It’s technically allowed but is contrary to the spirit of functional programming patterns and is dependent upon the implementation details of firstIndex.







            share|improve this answer









            $endgroup$













            • $begingroup$
              FYI, the above is a simple refactoring of the code snippet in the question, but does not attempt to tackle the question of what changes are needed to handle arbitrary depth in the subarrays.
              $endgroup$
              – Rob
              Apr 3 at 8:40














            1












            1








            1





            $begingroup$

            I gather that your intent was to write a method that iterates through an array of arrays (but avoiding functional patterns), flattening the results in the process such that given...



            let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]


            ... that result for 5..<10 would be ["5", "6", "7", "8", "9"]



            Assuming that’s what you were trying to do, I think you can simplify it:



            extension Array {
            func flattened<T>(range: Range<Int>) -> [T]? where Element == Array<T> {
            var result: [T] = []

            var offset = range.startIndex
            var length = range.upperBound - range.lowerBound

            result.reserveCapacity(length)

            for subarray in self {
            let subarrayCount = subarray.count
            if offset < subarrayCount {
            if length > subarrayCount - offset {
            result += subarray[offset...]
            length -= subarrayCount - offset
            } else {
            return result + subarray[offset..<offset + length]
            }
            }
            offset = Swift.max(0, offset - subarrayCount)
            }

            return nil
            }
            }


            In terms of observations on your code:




            • I wouldn’t advise get method name. It’s not very meaningful name and only conjures up confusion with getters. I’d go with something that captured the “flattening” nature of the routine.


            • As a general rule, we should avoid closures with side-effects in functional programming patterns. Even though you’ve written an “iterative” rendition of the OP’s code, you’re using a functional method, firstIndex, and you are updating variables outside the closure. It’s technically allowed but is contrary to the spirit of functional programming patterns and is dependent upon the implementation details of firstIndex.







            share|improve this answer









            $endgroup$



            I gather that your intent was to write a method that iterates through an array of arrays (but avoiding functional patterns), flattening the results in the process such that given...



            let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]


            ... that result for 5..<10 would be ["5", "6", "7", "8", "9"]



            Assuming that’s what you were trying to do, I think you can simplify it:



            extension Array {
            func flattened<T>(range: Range<Int>) -> [T]? where Element == Array<T> {
            var result: [T] = []

            var offset = range.startIndex
            var length = range.upperBound - range.lowerBound

            result.reserveCapacity(length)

            for subarray in self {
            let subarrayCount = subarray.count
            if offset < subarrayCount {
            if length > subarrayCount - offset {
            result += subarray[offset...]
            length -= subarrayCount - offset
            } else {
            return result + subarray[offset..<offset + length]
            }
            }
            offset = Swift.max(0, offset - subarrayCount)
            }

            return nil
            }
            }


            In terms of observations on your code:




            • I wouldn’t advise get method name. It’s not very meaningful name and only conjures up confusion with getters. I’d go with something that captured the “flattening” nature of the routine.


            • As a general rule, we should avoid closures with side-effects in functional programming patterns. Even though you’ve written an “iterative” rendition of the OP’s code, you’re using a functional method, firstIndex, and you are updating variables outside the closure. It’s technically allowed but is contrary to the spirit of functional programming patterns and is dependent upon the implementation details of firstIndex.








            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Apr 2 at 19:00









            RobRob

            52849




            52849












            • $begingroup$
              FYI, the above is a simple refactoring of the code snippet in the question, but does not attempt to tackle the question of what changes are needed to handle arbitrary depth in the subarrays.
              $endgroup$
              – Rob
              Apr 3 at 8:40


















            • $begingroup$
              FYI, the above is a simple refactoring of the code snippet in the question, but does not attempt to tackle the question of what changes are needed to handle arbitrary depth in the subarrays.
              $endgroup$
              – Rob
              Apr 3 at 8:40
















            $begingroup$
            FYI, the above is a simple refactoring of the code snippet in the question, but does not attempt to tackle the question of what changes are needed to handle arbitrary depth in the subarrays.
            $endgroup$
            – Rob
            Apr 3 at 8:40




            $begingroup$
            FYI, the above is a simple refactoring of the code snippet in the question, but does not attempt to tackle the question of what changes are needed to handle arbitrary depth in the subarrays.
            $endgroup$
            – Rob
            Apr 3 at 8:40













            0












            $begingroup$

            Extensibility



            The following is meant as an addendum to Rob's answer. It is built upon their approach and extends it to work for arrays with arbitrary depth.



            extension Array {
            func flattened(range: Range<Int>) -> [Any]? {
            return helper(range).result?.count == range.upperBound - range.lowerBound ?
            helper(range).result :
            nil
            }

            private func helper(_ range: Range<Int>) -> (result: [Any]?, offset: Int) {
            var result: [Any] = []

            var offset = range.startIndex
            let length = range.upperBound - range.lowerBound

            result.reserveCapacity(length)

            for i in self.indices {
            let element = self[i]

            if let sub_a: [Any] = element as? [Any] {
            let tempo = sub_a.helper(offset..<offset + length - result.count)
            if let res = tempo.result {
            result += res
            offset = tempo.offset
            } else {
            return (result: nil, offset: offset)
            }
            } else {
            if offset == 0 {
            result.append(element)
            }
            offset = Swift.max(0, offset - 1)
            }

            if result.count == length {
            return (result: result, offset: offset)
            }
            }

            return (result: result, offset: offset)
            }
            }


            It has been tested with all of these arrays :



            let a: [Any] = [[["0", "1", "2",   "3",  "4",    "5"], ["6",  "7"], []],
            [["8", "9", "10", "11", "12"], ["13", "14", "15"]]]

            let b: [Any] = [[["0", "1"], ["2", "3", "4"]],
            [[]],
            [["5"], ["6", "7", "8", "9"]],
            [["10", "11", "12"], ["13", "14", "15"]]]

            let c: [Any] = [["0", "1", "2", "3", "4", "5", ["6", "7", ["8", "9", "10", "11", "12"]]], [], ["13", "14", "15"]]


            The output of all three :



            print(a.flattened(range: 3..<15))
            print(b.flattened(range: 3..<15))
            print(c.flattened(range: 3..<15))


            is




            Optional(["3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14"])







            share|improve this answer









            $endgroup$


















              0












              $begingroup$

              Extensibility



              The following is meant as an addendum to Rob's answer. It is built upon their approach and extends it to work for arrays with arbitrary depth.



              extension Array {
              func flattened(range: Range<Int>) -> [Any]? {
              return helper(range).result?.count == range.upperBound - range.lowerBound ?
              helper(range).result :
              nil
              }

              private func helper(_ range: Range<Int>) -> (result: [Any]?, offset: Int) {
              var result: [Any] = []

              var offset = range.startIndex
              let length = range.upperBound - range.lowerBound

              result.reserveCapacity(length)

              for i in self.indices {
              let element = self[i]

              if let sub_a: [Any] = element as? [Any] {
              let tempo = sub_a.helper(offset..<offset + length - result.count)
              if let res = tempo.result {
              result += res
              offset = tempo.offset
              } else {
              return (result: nil, offset: offset)
              }
              } else {
              if offset == 0 {
              result.append(element)
              }
              offset = Swift.max(0, offset - 1)
              }

              if result.count == length {
              return (result: result, offset: offset)
              }
              }

              return (result: result, offset: offset)
              }
              }


              It has been tested with all of these arrays :



              let a: [Any] = [[["0", "1", "2",   "3",  "4",    "5"], ["6",  "7"], []],
              [["8", "9", "10", "11", "12"], ["13", "14", "15"]]]

              let b: [Any] = [[["0", "1"], ["2", "3", "4"]],
              [[]],
              [["5"], ["6", "7", "8", "9"]],
              [["10", "11", "12"], ["13", "14", "15"]]]

              let c: [Any] = [["0", "1", "2", "3", "4", "5", ["6", "7", ["8", "9", "10", "11", "12"]]], [], ["13", "14", "15"]]


              The output of all three :



              print(a.flattened(range: 3..<15))
              print(b.flattened(range: 3..<15))
              print(c.flattened(range: 3..<15))


              is




              Optional(["3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14"])







              share|improve this answer









              $endgroup$
















                0












                0








                0





                $begingroup$

                Extensibility



                The following is meant as an addendum to Rob's answer. It is built upon their approach and extends it to work for arrays with arbitrary depth.



                extension Array {
                func flattened(range: Range<Int>) -> [Any]? {
                return helper(range).result?.count == range.upperBound - range.lowerBound ?
                helper(range).result :
                nil
                }

                private func helper(_ range: Range<Int>) -> (result: [Any]?, offset: Int) {
                var result: [Any] = []

                var offset = range.startIndex
                let length = range.upperBound - range.lowerBound

                result.reserveCapacity(length)

                for i in self.indices {
                let element = self[i]

                if let sub_a: [Any] = element as? [Any] {
                let tempo = sub_a.helper(offset..<offset + length - result.count)
                if let res = tempo.result {
                result += res
                offset = tempo.offset
                } else {
                return (result: nil, offset: offset)
                }
                } else {
                if offset == 0 {
                result.append(element)
                }
                offset = Swift.max(0, offset - 1)
                }

                if result.count == length {
                return (result: result, offset: offset)
                }
                }

                return (result: result, offset: offset)
                }
                }


                It has been tested with all of these arrays :



                let a: [Any] = [[["0", "1", "2",   "3",  "4",    "5"], ["6",  "7"], []],
                [["8", "9", "10", "11", "12"], ["13", "14", "15"]]]

                let b: [Any] = [[["0", "1"], ["2", "3", "4"]],
                [[]],
                [["5"], ["6", "7", "8", "9"]],
                [["10", "11", "12"], ["13", "14", "15"]]]

                let c: [Any] = [["0", "1", "2", "3", "4", "5", ["6", "7", ["8", "9", "10", "11", "12"]]], [], ["13", "14", "15"]]


                The output of all three :



                print(a.flattened(range: 3..<15))
                print(b.flattened(range: 3..<15))
                print(c.flattened(range: 3..<15))


                is




                Optional(["3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14"])







                share|improve this answer









                $endgroup$



                Extensibility



                The following is meant as an addendum to Rob's answer. It is built upon their approach and extends it to work for arrays with arbitrary depth.



                extension Array {
                func flattened(range: Range<Int>) -> [Any]? {
                return helper(range).result?.count == range.upperBound - range.lowerBound ?
                helper(range).result :
                nil
                }

                private func helper(_ range: Range<Int>) -> (result: [Any]?, offset: Int) {
                var result: [Any] = []

                var offset = range.startIndex
                let length = range.upperBound - range.lowerBound

                result.reserveCapacity(length)

                for i in self.indices {
                let element = self[i]

                if let sub_a: [Any] = element as? [Any] {
                let tempo = sub_a.helper(offset..<offset + length - result.count)
                if let res = tempo.result {
                result += res
                offset = tempo.offset
                } else {
                return (result: nil, offset: offset)
                }
                } else {
                if offset == 0 {
                result.append(element)
                }
                offset = Swift.max(0, offset - 1)
                }

                if result.count == length {
                return (result: result, offset: offset)
                }
                }

                return (result: result, offset: offset)
                }
                }


                It has been tested with all of these arrays :



                let a: [Any] = [[["0", "1", "2",   "3",  "4",    "5"], ["6",  "7"], []],
                [["8", "9", "10", "11", "12"], ["13", "14", "15"]]]

                let b: [Any] = [[["0", "1"], ["2", "3", "4"]],
                [[]],
                [["5"], ["6", "7", "8", "9"]],
                [["10", "11", "12"], ["13", "14", "15"]]]

                let c: [Any] = [["0", "1", "2", "3", "4", "5", ["6", "7", ["8", "9", "10", "11", "12"]]], [], ["13", "14", "15"]]


                The output of all three :



                print(a.flattened(range: 3..<15))
                print(b.flattened(range: 3..<15))
                print(c.flattened(range: 3..<15))


                is




                Optional(["3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14"])








                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Apr 4 at 15:32









                ielyamaniielyamani

                372213




                372213






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216657%2fefficient-way-of-flat-mapping-a-range-of-a-multidimensional-random-access-collec%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

                    How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

                    Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...