Problem
Given an array of values how can I split it into sub-arrays
made of elements that are equal?
给定一个值数组,如何将其拆分为由相等元素组成的子数组?
Example
Given this array
鉴于此数组
let numbers = [1, 1, 1, 3, 3, 4]
I want this output
我想要这个输出
[[1,1,1], [3, 3], [4]]
What I am NOT looking for
A possible way of solving this would be creating some sort of index to indicate the occurrences of each element like this.
解决这个问题的一种可能方法是创建某种索引来指示每个元素的出现。
let indexes = [1:3, 3:2, 4:1]
And finally use the index to rebuild the output array.
最后使用索引重建输出数组。
let subsequences = indexes.sort { $0.0.0 < $0.1.0 }.reduce([Int]()) { (res, elm) -> [Int] in
return res + [Int](count: elm.1, repeatedValue: elm.0)
}
However with this solution I am losing the original values. Of course in this case it's not a big problem (an Int
value is still and Int
even if recreated) but I would like to apply this solution to more complex data structures like this
但是,使用此解决方案,我将丢失原始值。当然在这种情况下它不是一个大问题(Int值仍然是Inteven,如果重新创建)但我想将此解决方案应用于更复杂的数据结构,如此
struct Starship: Equatable {
let name: String
let warpSpeed: Int
}
func ==(left:Starship, right:Starship) -> Bool {
return left.warpSpeed == right.warpSpeed
}
Final considerations
The function I am looking for would be some kind of reverse of flatten()
, infact
我正在寻找的函数将是flatten()的某种反转,实际上
let subsequences: [[Int]] = [[1,1,1], [3, 3], [4]]
print(Array(subsequences.flatten())) // [1, 1, 1, 3, 3, 4]
I hope I made myself clear, let me know should you need further details.
我希望自己明确表示,如果您需要进一步的细节,请告诉我。
3 个解决方案
#1
10
// extract unique numbers using a set, then
// map sub-arrays of the original arrays with a filter on each distinct number
let numbers = [1, 1, 1, 3, 3, 4]
let numberGroups = Set(numbers).map{ value in return numbers.filter{$0==value} }
print(numberGroups)
[EDIT] changed to use Set Initializer as suggested by Hamish
[编辑]改为使用Hamish建议的Set Initializer
[EDIT2] Swift 4 added an initializer to Dictionary that will do this more efficiently:
[EDIT2] Swift 4为Dictionary添加了一个初始化程序,可以更有效地执行此操作:
let numberGroups = Array(Dictionary(grouping:numbers){$0}.values)
#2
3
If you could use CocoaPods/Carthage/Swift Package Manager/etc. you could use packages like oisdk/SwiftSequence which provides the group()
method:
如果你可以使用CocoaPods / Carthage / Swift Package Manager /等。你可以使用像oisdk / SwiftSequence这样提供group()方法的包:
numbers.lazy.group()
// should return a sequence that generates [1, 1, 1], [3, 3], [4].
or UsrNameu1/TraverSwift which provides groupBy
:
或者UsrNameu1 / TraverSwift提供groupBy:
groupBy(SequenceOf(numbers), ==)
If you don't want to add external dependencies, you could always write an algorithm like:
如果您不想添加外部依赖项,则可以始终编写如下算法:
func group<S: SequenceType where S.Generator.Element: Equatable>(seq: S) -> [[S.Generator.Element]] {
var result: [[S.Generator.Element]] = []
var current: [S.Generator.Element] = []
for element in seq {
if current.isEmpty || element == current[0] {
current.append(element)
} else {
result.append(current)
current = [element]
}
}
result.append(current)
return result
}
group(numbers)
// returns [[1, 1, 1], [3, 3], [4]].
#3
1
Let's assume that you have an unsorted array of items. You will need to sort the initial array then you will have something like this: [1, 1, 1, 3, 3, 4]
我们假设你有一个未排序的项目数组。您将需要对初始数组进行排序,然后您将得到类似的结果:[1,1,1,3,3,4]
After that you will initialize two arrays: one for storing arrays and another one to use it as a current array.
之后,您将初始化两个数组:一个用于存储数组,另一个用于将其用作当前数组。
Loop through the initial array and:
循环遍历初始数组并:
- if the current value isn't different from the last one, push it to the current array
- 如果当前值与最后一个值没有差别,则将其推送到当前数组
- otherwise push the current array to the first one then empty the current array.
- 否则将当前数组推送到第一个数组,然后清空当前数组。
Hope it helps!
希望能帮助到你!
#1
10
// extract unique numbers using a set, then
// map sub-arrays of the original arrays with a filter on each distinct number
let numbers = [1, 1, 1, 3, 3, 4]
let numberGroups = Set(numbers).map{ value in return numbers.filter{$0==value} }
print(numberGroups)
[EDIT] changed to use Set Initializer as suggested by Hamish
[编辑]改为使用Hamish建议的Set Initializer
[EDIT2] Swift 4 added an initializer to Dictionary that will do this more efficiently:
[EDIT2] Swift 4为Dictionary添加了一个初始化程序,可以更有效地执行此操作:
let numberGroups = Array(Dictionary(grouping:numbers){$0}.values)
#2
3
If you could use CocoaPods/Carthage/Swift Package Manager/etc. you could use packages like oisdk/SwiftSequence which provides the group()
method:
如果你可以使用CocoaPods / Carthage / Swift Package Manager /等。你可以使用像oisdk / SwiftSequence这样提供group()方法的包:
numbers.lazy.group()
// should return a sequence that generates [1, 1, 1], [3, 3], [4].
or UsrNameu1/TraverSwift which provides groupBy
:
或者UsrNameu1 / TraverSwift提供groupBy:
groupBy(SequenceOf(numbers), ==)
If you don't want to add external dependencies, you could always write an algorithm like:
如果您不想添加外部依赖项,则可以始终编写如下算法:
func group<S: SequenceType where S.Generator.Element: Equatable>(seq: S) -> [[S.Generator.Element]] {
var result: [[S.Generator.Element]] = []
var current: [S.Generator.Element] = []
for element in seq {
if current.isEmpty || element == current[0] {
current.append(element)
} else {
result.append(current)
current = [element]
}
}
result.append(current)
return result
}
group(numbers)
// returns [[1, 1, 1], [3, 3], [4]].
#3
1
Let's assume that you have an unsorted array of items. You will need to sort the initial array then you will have something like this: [1, 1, 1, 3, 3, 4]
我们假设你有一个未排序的项目数组。您将需要对初始数组进行排序,然后您将得到类似的结果:[1,1,1,3,3,4]
After that you will initialize two arrays: one for storing arrays and another one to use it as a current array.
之后,您将初始化两个数组:一个用于存储数组,另一个用于将其用作当前数组。
Loop through the initial array and:
循环遍历初始数组并:
- if the current value isn't different from the last one, push it to the current array
- 如果当前值与最后一个值没有差别,则将其推送到当前数组
- otherwise push the current array to the first one then empty the current array.
- 否则将当前数组推送到第一个数组,然后清空当前数组。
Hope it helps!
希望能帮助到你!