【问题标题】:Combining multiple arrays into one, indexing sequentially将多个数组合二为一,按顺序索引
【发布时间】:2017-02-03 09:59:11
【问题描述】:

我有多个数组(最多 15 个),每个数组最多可以有 1800 个对象。

我需要将它们组合成一个数组,这样我就可以应用分隔符 (',') 来生成 csv 文件。问题是当我将它们组合成一个数组时,它必须按顺序组合,就像每个数组的第一个对象应该首先插入,然后是对象的第二个索引,然后是第三个等等。

我可以通过使用 for-in 循环来实现我想要的结果。但是,这并不是很迅速。我觉得可以使用 swift 中可用的函数方法(使用 map、reduce 和 filter 函数)以更简洁的方式完成。

但是我无法将它们完美地结合起来。任何人都可以帮助我使用 swift 函数方法来实现结果。

P.S:如果您希望我发布 for-in 循环代码,请告诉我,但我相信这不是必需的。

【问题讨论】:

  • 是的,看到了。我也在尝试不同的解决方案,因为这似乎是一个好问题。如果我在周末找到更好的答案,将更新我的答案,否则将接受下面给出的答案之一。干杯!! :)

标签: arrays swift functional-programming


【解决方案1】:

这是我对您在帖子中描述的问题的实用方法。

首先我使用枚举方法对数组进行展平,该方法返回一个元组,其中包含数组中的元素位置及其值。

在此之后,您有一个包含此元组的数组,下一步,按每个元素的偏移(位置)值对这个大数组进行排序。

一旦对数组进行排序,您就可以提取使用 map 函数的值。

最后一步,一旦我们有了一个带有排序值的数组,你必须使用 reduce 函数将它减少为一个字符串

// A group of arrays
var array1: [Int] = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
var array2: [Int] = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
var array3: [Int] = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
var array4: [Int] = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

// This is **your** array
let bigOne = [ array1, array2, array3, array4 ]

// And here is the functional concatenation.
let flattedOne = bigOne.flatMap({ $0.enumerated() }).sorted(by: { $0.0 < $0.1 }).map({$0.element}).reduce("") 
{
    return $0.isEmpty ? "\($1)" : "\($0), \($1)"

}


print(flattedOne)

【讨论】:

  • 我尝试过这个,它在排序后给出了错误的输出。我更改了相同的值以包含不同的值。例如:第一个数组包含 1、2、3...;第二个数组包含 10, 20, 30... ;第三个 100, 200, 300,.. 和第四个 1000, 2000, 3000... 等等。排序后的函数就像 1, 10, 1000, 100 应该是 1, 10, 100, 1000。顺序这里出错了。
  • 嘿,你能在这里和我们聊天吗--> chat.stackoverflow.com/rooms/124193/…
【解决方案2】:

给定 4 个(或更多)数组

let list0: [Int] = [ 1, 2, 3, 6, 7, 8, 9 ]
let list1: [Int] = [ 10, 20, 30, 40, 50, 60, 70, 80, 90]
let list2: [Int] = [ 100, 200, 300, 400, 500, 600, 700, 800, 900]
let list3: [Int] = [ 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000 ]

枚举每一个并将它们放入另一个数组中

let lists = [
    list0.enumerate().map { (index: $0, array: 0, value: $1) },
    list1.enumerate().map { (index: $0, array: 1, value: $1) },
    list2.enumerate().map { (index: $0, array: 2, value: $1) },
    list3.enumerate().map { (index: $0, array: 3, value: $1) }
]

现在你可以写了

let sorted = lists
    .flatten()
    .sort { ($0.index, $0.array) < ($1.index, $1.array) }
    .map { $0.value }

[1, 10, 100, 1000, 2, 20, 200, 2000, 3, 30, 300, 3000, 6, 40, 400, 4000, 7, 50, 500, 5000, 8, 60, 600, 6000、9、70、700、7000、80、800、8000、90、900、9000]

【讨论】:

  • 完美。我也尝试了不同的值和不同大小的数组。按预期工作。非常感谢。将最后一行更新为 swift 3.0 语法。
  • 它工作得很好,但是仔细观察后我仍然有一个疑问,排序功能的条件是如何工作的? { $0.0
  • @Rameswar:事实上你是对的! $0.0 &lt; $0.1 应该是 $0.0 &lt; $1.0。谢谢你。刚刚更新了我的代码。
  • 等等,但这并没有给出正确的答案。我已经测试过了。顺序从 1、10、100、1000 变为 1、10、1000、100。
  • @Rameswar: $0.0 &lt; $1.0 给了我这个:[1, 10, 100, 2, 20, 200, 3, 30, 300]
【解决方案3】:

我会考虑将其作为数组数组的扩展(尽管请注意您不能直接这样做,请参阅this Q&A)。然后,您可以使用reduce(_:_:)flatMap(_:) 两种风格的组合,以便通过遍历内部集合的长度并提取每个给定索引处的元素来顺序合并您的数组。

extension Array where Element : RandomAccessCollection, Element.Index == Int, Element.IndexDistance == Element.Index {

    func joinedByTransposing() -> [Element.Iterator.Element] {

        // The maximum length of the inner collections. Obviously if the 2D array is
        // guaranteed to be n*m, you can optimise by just taking the first inner
        // collection's count (and obviously you'll want to check that the array isn't empty first).
        let maxIndex = self.reduce(0, {$0 > $1.count ? $0 : $1.count})

        // Iterates through the max length of the inner collections, joining the restantant collections
        // from the transform below into a single array.
        return (0..<maxIndex).flatMap { index in

            // Iterate through each inner collection, getting the element at the current index of iteration,
            // or returning nil if the index is out of bounds. This flatMap will filter out any nils.
            // If the 2D array is guarenteed to be n*m, this can be replaced by self.map { $0[index] }
            self.flatMap { innerArray in

                // Simple bounds check to get the element at the given index, or nil if out of bounds
                index < innerArray.count ? innerArray[index] : nil
            }
        }
    }
}

let array0 = [1,   2,   3,   4   ]
let array1 = [10,  20,  30       ]
let array2 = [100, 200, 300, 6, 7]

let result = [array0, array1, array2].joinedByTransposing()

print(result)

// [1, 10, 100, 2, 20, 200, 3, 30, 300, 4, 6, 7]

值得注意的是,该解决方案的总体时间复杂度为 O(n * m) - 而使用 sorted(by:) 的解决方案的时间复杂度至少为 O(n * m * log(n * m)) .对于大型数组,这个额外的成本可能很重要。

【讨论】:

    【解决方案4】:

    这是另一种方法...

    public func sort(compound array: [[Int]]) -> [Int]
    {
        let max_index: Int = array.map({ $0.count }).max()!
    
        var sorted: [Int] = [Int]()
    
    
        (0 ..< max_index).forEach({ index in
            array.forEach()
            { 
                if $0.count > index
                {
                    sorted.append($0[index])
                }
            }
        })
    
       return sorted
    }
    
    
    // A group of arrays
    var array1: [Int] = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 111, 112 ]
    var array2: [Int] = [ 10, 20, 3, 4, 5, 6, 7, 8, 9, 10 ]
    var array3: [Int] = [ 1000, 2000, 3, 4, 5, 600, 7, 8, 9, 10, 11 ]
    var array4: [Int] = [ 100, 200, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ]
    
    let bigOne: [[Int]] = [ array1, array2, array3, array4 ]
    
    let sorted: [Int] = sort(compound: bigOne)
    print(sorted)
    

    如果您希望将数组作为 CSV 字符串...

    print(sorted.reduce("") { return $0.isEmpty ? "\($1)" : "\($0), \($1)" })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多