Swift remove item in array - arrays

I have a Problem with SwityJson and removing an Element of an Array
For further Datahandling i must remove some Elements.
here my Code
self.jsonObj = JSON(value)
//Filtern
for i in 0 ..< self.jsonObj["customer"].count {
if self.jsonObj["customer"][i]["Group_A"].string == "Mr Abcde"
|| self.jsonObj["customer"][i]["Group_A"].string == "Mr Bcde"
|| self.jsonObj["custome"][i]["Group_B"].string == "Mr Abcde"
|| self.jsonObj["customer"][i]["Group_B"].string == "Mr Bcde"
{
self.jsonObj["customer"].arrayObject?.removeAtIndex(i)
}
}
Now the Problem: If running the Code not all found Element are removed.
I think that looping through all Elements is too fast. There is noch Time for the removing Task?!
How can I handle it. Looping...found something...stop looping...remove Item...start looping..
By making the If-Statement for three Times all is fine an all found Elements are removed, but hey, this is not what i want.
Or is it possible to filter the Array an then say
filterdData = jsonObj

Try
//Filtern
for i in 0 ..< self.jsonObj["customer"].count {
let groupA = self.jsonObj["customer"][i]["Group_A"].string
let groupB = self.jsonObj["customer"][i]["Group_B"].string
if groupA == "Mr Abcde" || groupA == "Mr Bcde" || groupB == "Mr Abcde" || groupB == "Mr Bcde"{
self.jsonObj["customer"].rawArray.removeAtIndex(i)
}
}
Not only does this have less calls to the JSON object(potentially saving speed) but it also uses rawArray instead of arrayObject which writes directly the array instead of going through the arrayObject to the rawArray.

The problem is that you're changing the count that terminates the loop from within the loop. You need to not do that, perhaps using filter or a related approach instead

I would recommend normalizing the data before modifying it. But failing that you could try this:
let filteredJson = self.jsonObj["customer"].filter { elem in
!(["Mr Abcde", "Mr Bcde"].contains(elem.["Group_A"].string) ||
["Mr Abcde", "Mr Bcde"].contains(elem.["Group_B"].string))
}
and then use the filteredJson constant.

Related

Sorting array with starter inside the array

sorry I had issues formulating my question.
to explain it better :
I have a = ["alice", "jean", "bob"]
Now i want to let the user choose who will start the game.
If its jean, the new array should be like this
a = ["jean", "bob", "alice"]
So far, this is working :
def sort_array_players(array_player, starter)
sort_array_player = []
array_player.map do |name|
if name == starter && name == array_player[0]
sort_array_player = [array_player[0], array_player[1], array_player[2]]
elsif name == starter && name == array_player[1]
sort_array_player = [array_player[1], array_player[2], array_player[0]]
elsif name == starter && name == array_player[2]
sort_array_player = [array_player[2], array_player[0], array_player[1]]
end
end
puts sort_array_player
end
I want to refractor this code but i'm a bit new to ruby, I've spend 2 hours trying to figure out this thing. My guess is that you need to use each.with_index and then create the new array starting by the first one and the following element would be the one with the index of the starter + 1..
Thanks for helping guys
As #rubish commented, you can use Array#rotate method.
You can use positive integer as count parameter to rotate items counter clockwise or negative integers to rotate them clockwise.
a = ["alice", "jean", "bob"]
starter = "jean"
count = a.index(starter) # => 1
a.rotate(count) # => ["jean", "bob", "alice"]

Generate random Strings without a shuffle and repetition of previous ones?

My code already generates a random String of an array when I press a button, but sometimes a String gets repeated. What do I have to do so that the String "Mango" only gets called again when all the other Strings where already called without using a shuffle, I want to call one String at a time?
Example: "Mango", "Kiwi", "Banana", "Pineapple", "Melon", "Mango", "Kiwi",.....
Here is my code:
var array = ["Mango", "Banana", "Apple","Kiwi", "Melon", "Pineapple"]
let fruits = Int(arc4random_uniform(UInt32(array.count)))
print(array[fruits])
In order to avoid repetitions you need to keep track of which fruits have previously been seen. There are several ways to do that an all of the proposed solutions do it in one way or another.
For your specific use case, you will need this tracking to be retained outside of the code executed by the button (in your view controller for example).
Here is a generalized structure that could help with this:
(you can define it inside the view controller if this is a one-time thing or outside of it if you intend to reuse the mechanism elsewhere)
struct RandomItems
{
var items : [String]
var seen = 0
init(_ items:[String])
{ self.items = items }
mutating func next() -> String
{
let index = Int(arc4random_uniform(UInt32(items.count - seen)))
let item = items.remove(at:index)
items.append(item)
seen = (seen + 1) % items.count
return item
}
}
To use it, you would declare a variable (in your VC) that will keep track of your fruits:
var fruits = RandomItems(["Mango", "Banana", "Apple","Kiwi", "Melon", "Pineapple"])
And in the button's code use that variable (fruits) to print a non-repeating fruit name at each execution
func buttonPressed() // <- use your function here
{
print( fruits.next() )
}
You need to implement some logic. It's quite easy if you think harder. Run this in your Playground, or if you fully understand this block of code, you can do this in your project already.
var array = ["Mango", "Banana", "Apple","Kiwi", "Melon", "Pineapple"]
var selectedIndices = [Int]()
for _ in 1...20 {
let randomFruitIndex = Int(arc4random_uniform(UInt32(array.count)))
// Print only if not yet printed once
if !selectedIndices.contains(randomFruitIndex) {
print(array[randomFruitIndex])
selectedIndices.append(randomFruitIndex)
}
// Reset
if selectedIndices.count == array.count {
print("----- CLEARING SELECTED INDICES----")
selectedIndices.removeAll()
}
}
So as you can see, we are adding each generated random number (in your case, it's the fruits variable.) into an array of Int. Then if the number of selectedIndices is equal to the count of the array of fruits, clear all the stored selectedIndices.
OUTPUT:
Pineapple
Melon
Mango
Kiwi
Banana
Apple
----- CLEARING SELECTED INDICES----
Mango
Melon
This is an adaption from the accepted answer of the linked topic in my comment:
var source = ["Mango", "Banana", "Apple","Kiwi", "Melon", "Pineapple"]
var usedElements = [String]()
func choosePseudoRandomElement() -> String {
if source.count == 0 {
source = usedElements
usedElements = []
}
let randomIndex = Int(arc4random_uniform(UInt32(source.count)))
let randomItem = source[randomIndex]
usedElements.append(randomItem)
source.remove(at: randomIndex)
return randomItem
}
for _ in 1...18 {
print("Item: \(choosePseudoRandomElement())")
}
One potential issue with this solution is that it may happen, that the last element of one complete iteration also occurs as the first element of the second iteration. You can handle that case by comparing the randomly chosen item with the item which was chosen before (use a while loop until the items doesn't match anymore).
Also, this does remove elements from the source array. If you do not want that, create a copy of the source array.

swift - using .map on struct array

i have a struct array that i want "break up" into smaller arrays that can be called as needed or at least figure out how i can map the items needed off one text value.
the struct:
struct CollectionStruct {
var name : String
var description : String
var title : String
var image : PFFile
var id: String
}
and the array made from the struct
var collectionArray = [CollectionStruct]()
var i = 0
for item in collectionArray {
print(collectionArray[i].name)
i += 1
}
printing partArray[i].name gives the following result:
pk00_pt01
pk00_pt02
pk00_pt03
pk01_pt01
pk01_pt02
pk01_pt03
pk01_pt04
pk01_pt05
pk01_pt06
pk01_pt07
pk01_pt08
this is just some test values but there could be thousands of entries here so i wanted to filter the entire array just by the first 4 characters of [i].name i can achieve this by looping through as above but is this achievable using something like .map?
I wanted to filter the entire array just by the first 4 characters of
[i].name
You can achieve this by filtering the array based on the substring value of the name, as follows:
let filteredArray = collectionArray.filter {
$0.name.substring(to: $0.name.index($0.name.startIndex, offsetBy: 4)).lowercased() == "pk00"
// or instead of "pk00", add the first 4 characters you want to compare
}
filteredArray will be filled based on what is the compared string.
Hope this helped.
If you want to group all data automatically by their name prefix. You could use a reducer to generate a dictionary of grouped items. Something like this:
let groupedData = array.reduce([String: [String]]()) { (dictionary, myStruct) in
let grouper = myStruct.name.substring(to: myStruct.name.index(myStruct.name.startIndex, offsetBy: 4))
var newDictionart = dictionary
if let collectionStructs = newDictionart[grouper] {
newDictionart[grouper] = collectionStructs + [myStruct.name]
} else {
newDictionart[grouper] = [myStruct.name]
}
return newDictionart
}
This will produce a dictionary like this:
[
"pk00": ["pk00_pt01", "pk00_pt02", "pk00_pt03"],
"pk01": ["pk01_pt01", "pk01_pt02", "pk01_pt03", "pk01_pt04", "pk01_pt05", "pk01_pt06", "pk01_pt07"],
"pk02": ["pk02_pt08"]
]
Not sure if i am understanding you correctly but it sounds like you are looking for this...
To create a new array named partArray from an already existing array named collectionArray (that is of type CollectionStruct) you would do...
var partArray = collectionArray.map{$0.name}

Sort array in Swift Ascending with empty items

In Xcode (Swift) I have an array that is initialized to 100 empty items:
var persons = [String](count:100, repeatedValue: "")
With some functions I add content to the places in the array, starting at 0.
So for example my array is at some given moment:
["Bert", "Daniel", "Claire", "Aaron", "", "", ... ""]
With the dots representing the rest of the empty items. I use this function for sorting my array alphabetically:
persons = persons.sorted {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
This gives me an array back like this:
["", "", ... , "Aaron", "Bert", "Claire", "Daniel"]
What I want is to sort my array alphabetically but not with the empty items at the front. I need to get an array back like:
["Aaron", "Bert", "Claire", "Daniel", "", "", ... , ""]
For my part, I do not want an array with empty items but I found I couldn't add a value to my array if I did not declare like a 100 items (the array won't be filled to a 100 items, that's for sure).
Can anyone help me out?
As #Antonio said, it looks like you need a descending order set of strings. Besides the Dictionary method in #Antonio's answer (which works great), you can also use NSMutableSet (bridged from Objective-C):
let personSet = NSMutableSet()
personSet.addObject("Aaron")
personSet.addObject("Daniel")
personSet.addObject("Claire")
personSet.addObject("Aaron")
personSet.addObject("Bert")
personSet.addObject("Bert")
personSet.addObject("Joe")
personSet.removeObject("Joe") // You can remove too of course
Which creates the set:
{(
Claire,
Aaron,
Daniel,
Bert
)}
Then, when you want the people as an Array, you can use the allObjects cast to a [String]:
personSet.allObjects as [String]
And you can sort it just like you're currently doing:
let people = (personSet.allObjects as [String]).sort {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
Which makes people:
[Aaron, Bert, Claire, Daniel]
For those wondering how to sort the Array as originally stated in the question (Ascending but with empty strings at the end), that can be done with a little bit of custom logic in the sort function:
var persons = ["Bert", "Daniel", "Claire", "Aaron", "", "", ""]
persons.sort { (a, b) -> Bool in
if a.isEmpty {
return false
} else if b.isEmpty {
return true
} else {
return a.localizedCaseInsensitiveCompare(b) == .OrderedAscending
}
}
Result:
["Aaron", "Bert", "Claire", "Daniel", "", "", ""]
Reading comments in your question and other answers, I realize that you need a ordered set, containing unique values. There's no built in data structure in swift for that, but it can be easily be done by using a dictionary: simply use the string value as dictionary key, and a boolean as dictionary value - this ensures that keys are unique:
var persons = [String : Bool]()
persons["Bert"] = true
persons["Daniel"] = true
persons["Clair"] = true
persons["Clair"] = true
persons["Aaron"] = true
persons["Daniel"] = true
persons["Clair"] = true
You can quickly verify that with the above code the dictionary contains 4 elements only.
Next, obtain a copy of the dictionary keys (as an array):
var values = persons.keys.array
and sort it:
values.sort { $0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
Alternatively, if you want to stick with the fixed sized array, you can remove the empty items before sorting:
persons = persons
.filter( { $0.isEmpty == false } )
.sorted {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
I think you are confused about arrays. Swift arrays are not statically allocated structures which must be allocated and filled to maximum design capacity. Below is a crude example of how you can accomplish most of what you are expressing here. However, I really think that a dictionary is better suited to your needs.
var persons = [String]()
var inputData = ["Bert", "Daniel", "Bert", "Claire", "Aaron"]
for item in inputData {
var found = false
for existing in persons {
if existing == item {
found = true
break
}
}
if (!found) {
persons.append(item)
}
}
persons.sort{$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
println(persons)

Adding loop to array

I'm trying to add the results of a loop to an array.
Here is my code:
<cfset mobNumbers = ArrayNew(1)>
<cfloop query = "staffLoop">
<cfscript>
mobileNo = staffLoop.mobileno;
mobileNo = mobileNo.replaceAll("[^0-9]", "");
ext = staffLoop.extension;
ext = ext.replaceAll("[^0-9]", "");
if (Left(mobileNo, 2) == "07" || Left(mobileNo, 3) == "447") {
recipient = mobileNo;
} else if (Left(ext, 2) == "07" || Left(ext, 3) == "447") {
recipient = ext;
} else {
recipient = "";
}
if (Left(recipient, 2) == "07") {
recipient = "447" & Right(recipient, Len(recipient) - 2);
}
if (Len(recipient) == 12) {
[send text code]
}
</cfscript>
</cfloop>
<cfset ArrayAppend(mobNumbers, "recipient")>
The goal is to get an array of all mobile numbers.
My code isn't working and I'm after some research, I'm not sure what to do. Any ideas?
If possible I'd like to use non-cfscript for my solution but if using cfscript is easier, that's fine.
As Adam points out, the ArrayAppend needs to be inside the loop. You'll also need to remove the quotes around the "recipient" in your call to ArrayAppend, otherwire you'll have an array of the String "recipient".
Your arrayAppend() needs to be inside the loop, otherwise you're just appending the last result after the loop has finished.

Resources