Divide array into subarrays - arrays

I have the following array, I have to make sure to divide it in this way into subarray, taking into consideration the first part of the name followed by / as a criterion for subdivision, for example "name/other".
Can you give me a hand?
var a = ["origin/a", "origin/b", "origin/c", "remo/a", "remo/d", "remo/c", "next/g"]
var b = {
origin: ["a", "b", "c"],
remo: ["a", "d", "c"],
next: ["g"]
}

You could used reduce(into:_:) to do so:
let reduced = a.reduce(into: [String: [String]]()) { partialResult, currentTerm in
let components = currentTerm.components(separatedBy: "/")
guard components.count == 2 else { return }
partialResult[components[0]] = partialResult[components[0], default: [String]()] + [components[1]]
}
print(reduced)
Output:
$>["remo": ["a", "d", "c"], "next": ["g"], "origin": ["a", "b", "c"]]

One idea is like this:
First we need to separate the keys for the dictionary and all the values that need to be gathered together:
let keysValues = a
.map { $0.components(separatedBy: "/") }
.compactMap { components -> (String, String)? in
guard components.count == 2 else { return nil }
return (components.first!, components.last!)
}
Now we need to reduce that into a dictionary of [String: [String]] by grouping together the values for each key:
var dict: [String: [String]] = [:]
let answer = keysValues.reduce(into: dict) { (d, kv) in
let (k, v) = kv
d[k, default: []] += [v]
}

Related

How can I remove duplicate strings from an array and count them in Swift?

want to print out duplicate characters first and the next number of characters.
like from ["a","a","b","c","c"] to ["a", "2","c","2"].
However, I properly didn't solve this. want you to give me feedback on my code or let me know your wisdom how can you solve this.
import Foundation
var m : [String] = ["a","a","b","c","c"]
var count : Int = 0
var result : [String] = []
for i in 0..<m.count{
for word in m {
if m[i] == word{
count += 1
result.append(word)
}else{
count = 0
}
if count > 1{
result.append(String(count))
}
}
}
print("\(count)")
print("\(result)")
//["a", "a", "2", "a", "a", "2", "b", "c", "c", "2", "c", "c", "2"]
print(["a", "2","c","2"])// want to print it on the console like this.
There is a type for it, NSCountedSet
let m = ["a","a","b","c","c"]
let countedSet = NSCountedSet(array: m)
var result = [String]()
countedSet.forEach {
let count = countedSet.count(for: $0)
if count > 1 {
result.append(contentsOf: [$0 as! String, String(count)])
}
}
print(result)
You can get directly get duplicate value by below method
let fiteredValue = arrayList.filterDuplicates(includeElement: {$0 == $1})

SwiftUI - Indexset to index in array

I am using ForEach within a NavigationView and list combined with a function called when the user deletes a row using .onDelete() as per below.
struct PeriodListView: View {
#ObservedObject var theperiodlist = ThePeriodList()
#EnvironmentObject var theprofile: TheProfile
#State private var showingAddPeriod = false
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}
var body: some View {
NavigationView {
List {
ForEach(theperiodlist.periods) {period in
PeriodRow(period: period)
}
.onDelete(perform: removePeriods)
}
.navigationBarTitle("Periods")
.navigationBarItems(trailing:
Button(action: {self.showingAddPeriod = true}) {
Image(systemName: "plus")
}
)
.sheet(isPresented: $showingAddPeriod) {
AddPeriod(theperiodlist: self.theperiodlist).environmentObject(self.theprofile)
}
}
}
func removePeriods(at offsets: IndexSet) {
AdjustProfileRemove(period: theperiodlist.periods[XXX])
theperiodlist.periods.remove(atOffsets: offsets)
}
I have a separate function (AdjustProfileRemove(period)) which I want to call with the removed period as the variable - e.g. I want to find XXX in AdjustProfileRemove(period: theperiodlist.periods[XXX]). Is there a simple way to do this (I am guessing from IndexSet) or am I missing something fundamental?
Thanks.
.onDelete is declared as
#inlinable public func onDelete(perform action: ((IndexSet) -> Void)?) -> some DynamicViewContent
IndexSet is simply Set of all indexes of the elements in the array to remove. Let try this example
var arr = ["A", "B", "C", "D", "E"]
let idxs = IndexSet([1, 3])
idxs.forEach { (i) in
arr.remove(at: i)
}
print(arr)
so the resulting arr is now
["A", "C", "D"]
The reason, why .onDelete use IndexSet is that more than one row in List could be selected for delete operation.
BE CAREFULL! see the resulting array! Actually removing elements one by one needs some logic ...
Let's try
var arr = ["A", "B", "C", "D", "E"]
let idxs = IndexSet([1, 3])
idxs.sorted(by: > ).forEach { (i) in
arr.remove(at: i)
}
print(arr)
it works now as you expected, is it? the result now is
["A", "C", "E"]
Based on
theperiodlist.periods.remove(atOffsets: offsets)
it seems, that the ThePeriodList already has build-in function with required functionality.
in your case just replace
AdjustProfileRemove(period: theperiodlist.periods[XXX])
with
offsets.sorted(by: > ).forEach { (i) in
AdjustProfileRemove(period: theperiodlist.periods[i])
}
Here is possible approach (taking into account that in general offsets can contain many indexes)
func removePeriods(at offsets: IndexSet) {
theperiodlist.periods =
theperiodlist.periods.enumerated().filter { (i, item) -> Bool in
let removed = offsets.contains(i)
if removed {
AdjustProfileRemove(period: item)
}
return !removed
}.map { $0.1 }
}

Merge several arrays into one array

How can I merge several arrays into one 2D array?
Given I have the following input:
var arr1 = ["1", "2", "3"]
var arr2 = ["a", "b", "c"]
var arr3 = ["aa", "bb", "cc"]
, I need to have the output like this:
[["1", "a", "aa"], ["2", "b", "bb"], ["1", "c", "cc"]]
I think what you want is to combine the three arrays into a 2D array, and then transpose it.
To transpose a 2D array, you can find many solutions in this question.
This uses Crashalot's solution in the comments:
fileprivate func transpose<T>(input: [[T]]) -> [[T]] {
if input.isEmpty { return [[T]]() }
let count = input[0].count
var out = [[T]](repeating: [T](), count: count)
for outer in input {
for (index, inner) in outer.enumerated() {
out[index].append(inner)
}
}
return out
}
var arr1 = ["1", "2", "3"]
var arr2 = ["a", "b", "c"]
var arr3 = ["aa", "bb", "cc"]
transpose(input: [arr1, arr2, arr3])
If you want a more swifty transpose, you can use this (modified from here):
extension Collection where Self.Element: RandomAccessCollection {
func transposed() -> [[Self.Iterator.Element.Iterator.Element]]? {
guard Set(self.map { $0.count }).count == 1 else { return nil }
guard let firstRow = self.first else { return [] }
return firstRow.indices.map { index in
self.map{ $0[index] }
}
}
}
This is a naive expressive way, if you like.
[arr1,arr2,arr3].reduce([[],[],[]]) { result, next -> [[String]] in
return (0..<(next.count)).map{
var array = Array.init(result[$0])
array.append(next[$0]);
return array
}
}
or more directly:
[arr1,arr2,arr3].reduce(into: [[],[],[]]) { ( result : inout [[String]], next) in
_ = (0..<(next.count)).map{ result[$0].append(next[$0]);}
}
A variadic, FP-style, solution, that can pack an arbitrary number of arrays:
func pack<T>(_ arrays: [T]...) -> [[T]] {
guard !arrays.isEmpty else { return [] }
let minCount = arrays.map(\.count).min()!
return (0..<minCount).map { i in arrays.map { $0[i] } }
}
Usage:
let arr1 = ["1", "2", "3"]
let arr2 = ["a", "b", "c"]
let arr3 = ["aa", "bb", "cc"]
let result = pack(arr1, arr2, arr3)
// [["1", "a", "aa"], ["2", "b", "bb"], ["1", "c", "cc"]]
let anotherResult = pack(["firstName", "lastName"], ["John", "Doe"])
// [["firstName", "John"], ["lastName", "Doe"]]

Swift - How to get indexes of filtered items of array

let items: [String] = ["A", "B", "A", "C", "A", "D"]
items.whatFunction("A") // -> [0, 2, 4]
items.whatFunction("B") // -> [1]
Does Swift 3 support a function like whatFunction(_: Element)?
If not, what is the most efficient logic?
You can filter the indices of the array directly, it avoids the extra mapping.
let items = ["A", "B", "A", "C", "A", "D"]
let filteredIndices = items.indices.filter {items[$0] == "A"}
or as Array extension:
extension Array where Element: Equatable {
func whatFunction(_ value : Element) -> [Int] {
return self.indices.filter {self[$0] == value}
}
}
items.whatFunction("A") // -> [0, 2, 4]
items.whatFunction("B") // -> [1]
or still more generic
extension Collection where Element: Equatable {
func whatFunction(_ value : Element) -> [Index] {
return self.indices.filter {self[$0] == value}
}
}
You can create your own extension for arrays.
extension Array where Element: Equatable {
func indexes(of element: Element) -> [Int] {
return self.enumerated().filter({ element == $0.element }).map({ $0.offset })
}
}
You can simply call it like this
items.indexes(of: "A") // [0, 2, 4]
items.indexes(of: "B") // [1]
You can achieve this by chain of:
enumerated() - add indexes;
filter() out unnecessary items;
map() our indexes.
Example (works in Swift 3 - Swift 4.x):
let items: [String] = ["A", "B", "A", "C", "A", "D"]
print(items.enumerated().filter({ $0.element == "A" }).map({ $0.offset })) // -> [0, 2, 4]
Another way is using flatMap, which allows you to check the element and return index if needed in one closure.
Example (works in Swift 3 - Swift 4.0):
print(items.enumerated().flatMap { $0.element == "A" ? $0.offset : nil }) // -> [0, 2, 4]
But since Swift 4.1 flatMap that can return non-nil objects become deprecated and instead you should use compactMap.
Example (works since Swift 4.1):
print(items.enumerated().compactMap { $0.element == "A" ? $0.offset : nil }) // -> [0, 2, 4]
And the cleanest and the most memory-cheap way is to iterate through array indices and check if element of array at current index equals to required element.
Example (works in Swift 3 - Swift 5.x):
print(items.indices.filter({ items[$0] == "A" })) // -> [0, 2, 4]
In Swift 3 and Swift 4 you can do that:
let items: [String] = ["A", "B", "A", "C", "A", "D"]
extension Array where Element: Equatable {
func indexes(of item: Element) -> [Int] {
return enumerated().compactMap { $0.element == item ? $0.offset : nil }
}
}
items.indexes(of: "A")
I hope my answer was helpful 😊
you can use it like that :
let items: [String] = ["A", "B", "A", "C", "A", "D"]
let indexes = items.enumerated().filter {
$0.element == "A"
}.map{$0.offset}
print(indexes)
just copy and paste
extension Array {
func whatFunction(_ ids : String) -> [Int] {
var mutableArr = [Int]()
for i in 0..<self.count {
if ((self[i] as! String) == ids) {
mutableArr.append(i)
}
}
return mutableArr
}
}
You can use that below code:
var firstArray = ["k","d","r","r","p","k","b","p","k","k"]
var secondArray = ["k","d","r","s","d","r","b","c"]
let filterArray = firstArray.filter { secondArray.contains($0) }
let filterArray1 = firstArray.filter { !secondArray.contains($0) }
let filterIndex = firstArray.enumerated().filter { $0.element == "k" }.map { $0.offset }
print(filterArray) --> // ["k", "d", "r", "r", "k", "b", "k", "k"]
print(filterArray1) --> // ["p", "p"]
print(filterIndex) --> // [0, 5, 8, 9]
this can be a way too
// MARK: - ZIP: Dictionary like
let words = ["One", "Two", "Three", "Four"]
let numbers = 1...words.count
for (word, number) in zip(words, numbers) {
print("\n\(word): \(number)")
}
For example finding the indices of p_last values that are in inds1 array: (swift 4+)
let p_last = [51,42]
let inds1 = [1,3,51,42,4]
let idx1 = Array(inds1.filter{ p_last.contains($0) }.indices)
idx1 = [0,1]

Best way to move an item to the start of a collection

If I have a collection
let initial = [ "a", "b", "c", "d", "e" ]
and I wanted to move an item from that collection to the start (but keep the ordering of the other items intact)
let final = initial.placeFirst { $0 == "b" }
assert(final == [ "b", "a", "c", "d", "e" ])
What would be the best way to implement placeFirst?
My example has the elements as Equatable - that's just to make the question readable, it's sadly not the case in real life, hence a predicate passed into placeFirst which will return true for the item I want at the start.
For my use case there should only be one item which matches the predicate - if more than one matches then putting any (or some, or all) of the matching elements at the start is fine.
I have a few ideas, but it seems like the kind of problem there would be a really neat solution which uses bits of Collection/Sequence I'm not aware of yet.
PS I do realize how much this sounds like a homework question - I promise it's not :)
A possible implementation as a mutating method on RangeReplaceableCollection (Swift 3):
extension RangeReplaceableCollection {
mutating func placeFirst(where predicate: (Iterator.Element) -> Bool) {
if let index = index(where: predicate) {
insert(remove(at: index), at: startIndex)
}
}
}
Example:
var array = [ "a", "b", "c", "d", "e" ]
array.placeFirst(where: { $0 == "b" })
print(array) // ["b", "a", "c", "d", "e"]
Similar as in How do I shuffle an array in Swift? you can add a
non-mutating method taking an arbitrary sequence and returning an array:
extension Sequence {
func placingFirst(where predicate: (Iterator.Element) -> Bool) -> [Iterator.Element] {
var result = Array(self)
result.placeFirst(where: predicate)
return result
}
}
Example:
let initial = [ "a", "b", "c", "d", "e" ]
let final = initial.placingFirst { $0 == "b" }
print(final) // ["b", "a", "c", "d", "e"]
A possible implementation as a pair of mutating methods on MutableCollection (doesn't require the resizing of the collection):
extension MutableCollection {
mutating func placeFirst(from index: Index) {
var i = startIndex
while i < index {
swap(&self[i], &self[index]) // in Swift 4: swapAt(i, index)
formIndex(after: &i)
}
}
// in Swift 4, remove Iterator.
mutating func placeFirst(where predicate: (Iterator.Element) throws -> Bool) rethrows {
var i = startIndex
while i < endIndex {
if try predicate(self[i]) {
placeFirst(from: i)
}
formIndex(after: &i)
}
}
}
var initial = ["a", "b", "c", "d", "e", "c", "q"]
initial.placeFirst(where: { $0 == "c" })
print(initial) // ["c", "c", "a", "b", "d", "e", "q"]
In placeFirst(from:), we just take a single index, and swap all the elements from the start index up to the desired index, effectively placing the element at the given index at the start, and "shifting" the remaining elements up.
Then in the predicate version, placeFirst(where:), we iterate through and check the predicate against all the indices of the collection, calling onto placeFirst(from:) if we find a match.
And as Martin says, a non-mutating variant for all sequences can be created easily by first constructing an Array:
extension Sequence {
// in Swift 4, remove Iterator.
func placingFirst(
where predicate: (Iterator.Element) throws -> Bool
) rethrows -> [Iterator.Element] {
var result = Array(self)
try result.placeFirst(where: predicate)
return result
}
}
let initial = ["a", "b", "c", "d", "e", "c", "q"]
let final = initial.placingFirst(where: { $0 == "c" })
print(final) // ["c", "c", "a", "b", "d", "e", "q"]
In order to benchmark against Martin's implementation, I changed the implementation of my placeFirst(where:) to only consider the first element that the predicate matches, such that both implementations short-circuit:
extension MutableCollection {
mutating func placeFirstSwap(from index: Index) {
var i = startIndex
while i < index {
swapAt(i, index)
formIndex(after: &i)
}
}
mutating func placeFirstSwap(where predicate: (Iterator.Element) throws -> Bool) rethrows {
if let index = try index(where: predicate) {
placeFirstSwap(from: index)
}
}
}
extension RangeReplaceableCollection {
mutating func placeFirstInsertRemove(where predicate: (Iterator.Element) throws -> Bool) rethrows {
if let index = try index(where: predicate) {
insert(remove(at: index), at: startIndex)
}
}
}
extension Sequence {
func placingFirstInsertRemove(where predicate: (Iterator.Element) throws -> Bool) rethrows -> [Iterator.Element] {
var result = Array(self)
try result.placeFirstInsertRemove(where: predicate)
return result
}
func placingFirstSwap(where predicate: (Iterator.Element) throws -> Bool) rethrows -> [Iterator.Element] {
var result = Array(self)
try result.placeFirstSwap(where: predicate)
return result
}
}
Then, with the following setup in a Swift 4 release build:
import Foundation
let a = Array(0 ... 50_000_000)
let i = 33_000_000
print("pivot \(100 * Double(i) / Double(a.count - 1))% through array")
do {
let date = Date()
let final = a.placingFirstInsertRemove(where: { $0 == i })
print(final.count, "Martin's:", Date().timeIntervalSince(date))
}
do {
let date = Date()
let final = a.placingFirstSwap(where: { $0 == i })
print(final.count, "Hamish's:", Date().timeIntervalSince(date))
}
print("---")
do {
let date = Date()
let final = a.placingFirstInsertRemove(where: { $0 == i })
print(final.count, "Martin's:", Date().timeIntervalSince(date))
}
do {
let date = Date()
let final = a.placingFirstSwap(where: { $0 == i })
print(final.count, "Hamish's:", Date().timeIntervalSince(date))
}
When i is around 33_000_000, both implementations appear to have similar performance:
pivot 66.0% through array
50000001 Martin's: 0.344986021518707
50000001 Hamish's: 0.358841001987457
---
50000001 Martin's: 0.310263991355896
50000001 Hamish's: 0.313731968402863
With Martin's performing slightly better for values of i over this, e.g with i = 45_000_000:
pivot 90.0% through array
50000001 Martin's: 0.35604602098465
50000001 Hamish's: 0.392504990100861
---
50000001 Martin's: 0.321934998035431
50000001 Hamish's: 0.342424035072327
and mine performing slightly better for values of i less than this, e.g with i = 5_000_000:
pivot 10.0% through array
50000001 Martin's: 0.368523001670837
50000001 Hamish's: 0.271382987499237
---
50000001 Martin's: 0.289749026298523
50000001 Hamish's: 0.261726975440979
In all of these results, the second pair is generally more reliable, as both should benefit from branch prediction done by the first run.

Resources