I'm experiencing a strange thing when exporting environment variables in my test and pre-request scripts. Let's take the following code:
var temp = ["a", "b", "c"];
pm.environment.set("Array1", temp);
temp.length=0;
temp = ["1", "2", "3"];
pm.environment.set("Array2", temp);
temp.length=0;
temp = ["ZZ", "YY", "XX"];
pm.environment.set("Array3", temp);
console.log(pm.environment.get("Array1")); // expected = ["a", "b", "c"]
console.log(pm.environment.get("Array2")); // expected = ["1", "2", "3"]
console.log(pm.environment.get("Array3")); // expected = ["ZZ", "YY", "XX"]
I'm expecting all 3 arrays to have value right? Surprisingly the results are:
[]
[]
["ZZ", "YY", "XX"]
Only the last one is correct. And I can carry on with more arrays, everytime, only the last one gets really updated, all the other ones remain desperately empty. I don't understand what's wrong. Beside, I tried postman.setEnvironmentVariable instead of pm.environment.set and it worked find. Any idea?
Thank you.
FWIW, this isn't really a Postman question. It's a question about Javascript. I don't know anything about Postman but its pm.environment.set() method is obviously saving a reference to the array object rather than making a copy. When you subsequently do temp.length = 0; you truncate that array object so that it has zero elements. When you subsequently do temp = ['new', 'array']; you're creating a new array object and assigning a reference to it to the temp var. You then pass that new array object reference to the next pm.environment.set(). Note that the temp.length = 0; statement is unnecessary and the source of your problem.
See https://www.w3schools.com/js/js_arrays.asp
P.S., I'm curious what documentation you read that implied doing temp.length = 0; was the right thing to do.
Related
I'm doing some operations like sorting, filter and grouped by some attributes of arrays object.
I'm adding objects of a filtered array in to another array like:
arrGroup.append(contentsOf: filteredArray)
My question is: will all of the objects maintain the same sorted order in the array every time, with 100% certainty?
Logically, will it add the object like
for object in filteredArray {
arrGroup.append(object)
}
or
for index in 0...filteredArray.count {
let object = filteredArray[index]
arrGroup.append(object)
}
For me, all are same, just difference in CPU cycle at run time. But my friend says that I should go with last option. Technically I'm getting same result for all three every time I debug my code.
Your suggestion please.
Yes, when you add an Array to another Array it will maintain the order as it is.
But yes if you are using Set then you might not get same order as it is not ordered collection but Array is ordered collection which maintains it's ordering until you changes it manually.
here is the code example :
var arr1 = ["1", "2" , "3"] // Print arr1 : ["1", "2", "3"]
let arr2 = ["4", "5" , "6"] // Print arr2 : ["4", "5", "6"]
arr1.append(contentsOf: arr2) // Print arr1 : ["1", "2", "3", "4", "5", "6"]
Array preserves whatever ordering you give it.
Array.append(contentsOf:) appends all items of the second array to the end of the first array, in order. Here's roughly what that algorithm would look like:
extension Array {
mutating func myAppend(contentsOf other: [Element]) {
reserveCapacity(self.count + other.count)
for element in other {
self.append(element)
}
}
}
Techniques for iterating an array
If you only need the elements
The preferred method
The preferred way to iterate the items of a Sequence is to use a typical for-in loop:
for element in array { // most preferred!
// use the element
}
The discouraged method
for i in 0 ..< array.count {
let element = array[i] // Avoid this!
// use the element
}
I highly advise against this technique. The reason is because it's very easy to fall victim to an off-by-one-error. In fact, your very own example has it!
for index in 0...filteredArray.count {
let object = filteredArray[index] // when index is filteredArray.count ... đź’Ł
arrGroup.append(object)
}
Don't use this! Any array of n elements has indices 0 ..< n, not 0 ... n. Attempting to access array[array.count] will crash your program.
Another valid but discouraged method
for i in array.indices {
let element = array[i] // Avoid this!
// use the element
}
If you only need the indices
for i in array.indices {
// use the index i
}
If you need both the indices and the elements
for (i, element) in array.enumerated() {
// use the index i and the element.
}
The 2 for loops you have above are doing the same thing in terms of they will iterate from object #0 to the last object.
The first one is called fast enumeration and hence faster and more effective than the second.
And to answer your question. Yes the order will remain the same.
I have an array of strings for one variable, and a string as another variable. I'd like to append all of the strings in the collection to the single string.
So for example I have:
var s = String()
//have the CSV writer create all the columns needed as an array of strings
let arrayOfStrings: [String] = csvReport.map{GenerateRow($0)}
// now that we have all the strings, append each one
arrayOfStrings.map(s.stringByAppendingString({$0}))
the line above fails. I've tried every combination I can think of, but at the end of the day, I can't get it unless I just create a for loop to iterate through the entire collection, arrayOfStrings, and add it one by one. I feel like I can achieve this the same way using map or some other function.
Any help?
Thanks!
You can use joined(separator:):
let stringArray = ["Hello", "World"]
let sentence = stringArray.joined(separator: " ") // "Hello World"
You could convert your array to string using joinWithSeparator(String)
here is an example
var array = ["1", "2", "3"]
let stringRepresentation = array.joinWithSeparator("-") // "1-2-3"
source: [ How do I convert a Swift Array to a String? ]
There are at least two options here. The most semantic choice is likely joinWithSeparator on the [String] object. This concatenates every string in the array, placing the separator provided as a parameter between each string.
let result = ["a", "b", "c", "d"].joinWithSeparator("")
An alternative is to use a functional reduce and the + function operator which concatenates strings. This may be preferred if you want to do additional logic as part of the combine. Both example code produce the same result.
let result = ["a", "b", "c", "d"].reduce("", combine: +)
It's also worth noting the second options is transferrable to any type that can be added, whereas the first only works with a sequence of strings, as it is defined on a protocol extension of SequenceType where Generator.Element == String.
I’m new to Swift and have been having some troubles figuring out some aspects of Arrays and Dictionaries.
I have an array of dictionaries, for which I have used Type Aliases - e.g.
typealias myDicts = Dictionary<String, Double>
var myArray : [myDicts] = [
["id":0,
"lat”:55.555555,
"lng”:-55.555555,
"distance":0],
["id":1,
"lat": 44.444444,
"lng”:-44.444444,
"distance":0]
]
I then want to iterate through the dictionaries in the array and change the “distance” key value. I did it like this:
for dict:myDicts in myArray {
dict["distance"] = 5
}
Or even specifically making sure 5 is a double with many different approaches including e.g.
for dict:myDicts in myArray {
let numberFive : Double = 5
dict["distance"] = numberFive
}
All my attempts cause an error:
#lvalue $T5' is not identical to '(String, Double)
It seems to be acting as if the Dictionaries inside were immutable “let” rather than “var”. So I randomly tried this:
for (var dict:myDicts) in myArray {
dict["distance"] = 5
}
This removes the error and the key is indeed assigned 5 within the for loop, but this doesn't seem to actually modify the array itself in the long run. What am I doing wrong?
The implicitly declared variable in a for-in loop in Swift is constant by default (let), that's why you can't modify it directly in the loop.
The for-in documentation has this:
for index in 1...5 {
println("\(index) times 5 is \(index * 5)")
}
In the example above, index is a constant whose value is automatically
set at the start of each iteration of the loop. As such, it does not
have to be declared before it is used. It is implicitly declared
simply by its inclusion in the loop declaration, without the need for
a let declaration keyword.
As you've discovered, you can make it a variable by explicitly declaring it with var. However, in this case, you're trying to modify a dictionary which is a struct and, therefore, a value type and it is copied on assignment. When you do dict["distance"] = 5 you're actually modifying a copy of the dictionary and not the original stored in the array.
You can still modify the dictionary in the array, you just have to do it directly by looping over the array by index:
for index in 0..<myArray.count {
myArray[index]["distance"] = 5
}
This way, you're sure to by modifying the original dictionary instead of a copy of it.
That being said, #matt's suggestion to use a custom class is usually the best route to take.
You're not doing anything wrong. That's how Swift works. You have two options:
Use NSMutableDictionary rather than a Swift dictionary.
Use a custom class instead of a dictionary. In a way this is a better solution anyway because it's what you should have been doing all along in a situation where all the dictionaries have the same structure.
The "custom class" I'm talking about would be a mere "value class", a bundle of properties. This was kind of a pain to make in Objective-C, but in Swift it's trivial, so I now do this a lot. The thing is that you can stick the class definition for your custom class anywhere; it doesn't need a file of its own, and of course in Swift you don't have the interface/implementation foo to grapple with, let alone memory management and other stuff. So this is just a few lines of code that you can stick right in with the code you've already got.
Here's an example from my own code:
class Model {
var task : NSURLSessionTask!
var im : UIImage!
var text : String!
var picurl : String!
}
We then have an array of Model and away we go.
So, in your example:
class MyDict : NSObject {
var id = 0.0
var lat = 0.0
var lng = 0.0
var distance = 0.0
}
var myArray = [MyDict]()
let d1 = MyDict()
d1.id = 0
d1.lat = 55.55
d1.lng = -55.55
d1.distance = 0
let d2 = MyDict()
d2.id = 0
d2.lat = 44.44
d2.lng = -44.44
d2.distance = 0
myArray = [d1,d2]
// now we come to the actual heart of the matter
for d in myArray {
d.distance = 5
}
println(myArray[0].distance) // it worked
println(myArray[1].distance) // it worked
Yes, the dictionary retrieved in the loop is immutable, hence you cannot change.
I'm afraid your last attempt just creates a mutable copy of it.
One possible workaround is to use NSMutableDictionary:
typealias myDicts = NSMutableDictionary
Have a class wrapper for the Swift dictionary or array.
class MyDictionary: NSObject {
var data : Dictionary<String,Any>!
init(_ data: Dictionary<String,Any>) {
self.data = data
}}
MyDictionary.data
I want to enumerate through an array in Swift, and remove certain items. I'm wondering if this is safe to do, and if not, how I'm supposed to achieve this.
Currently, I'd be doing this:
for (index, aString: String) in enumerate(array) {
//Some of the strings...
array.removeAtIndex(index)
}
In Swift 2 this is quite easy using enumerate and reverse.
var a = [1,2,3,4,5,6]
for (i,num) in a.enumerate().reverse() {
a.removeAtIndex(i)
}
print(a)
You might consider filter way:
var theStrings = ["foo", "bar", "zxy"]
// Filter only strings that begins with "b"
theStrings = theStrings.filter { $0.hasPrefix("b") }
The parameter of filter is just a closure that takes an array type instance (in this case String) and returns a Bool. When the result is true it keeps the element, otherwise the element is filtered out.
In Swift 3 and 4, this would be:
With numbers, according to Johnston's answer:
var a = [1,2,3,4,5,6]
for (i,num) in a.enumerated().reversed() {
a.remove(at: i)
}
print(a)
With strings as the OP's question:
var b = ["a", "b", "c", "d", "e", "f"]
for (i,str) in b.enumerated().reversed()
{
if str == "c"
{
b.remove(at: i)
}
}
print(b)
However, now in Swift 4.2 or later, there is even a better, faster way that was recommended by Apple in WWDC2018:
var c = ["a", "b", "c", "d", "e", "f"]
c.removeAll(where: {$0 == "c"})
print(c)
This new way has several advantages:
It is faster than implementations with filter.
It does away with the need of reversing arrays.
It removes items in-place, and thus it updates the original array instead of allocating and returning a new array.
When an element at a certain index is removed from an array, all subsequent elements will have their position (and index) changed, because they shift back by one position.
So the best way is to navigate the array in reverse order - and in this case I suggest using a traditional for loop:
for var index = array.count - 1; index >= 0; --index {
if condition {
array.removeAtIndex(index)
}
}
However in my opinion the best approach is by using the filter method, as described by #perlfly in his answer.
No it's not safe to mutate arrays during enumaration, your code will crash.
If you want to delete only a few objects you can use the filter function.
Either create a mutable array to store the items to be deleted and then, after the enumeration, remove those items from the original. Or, create a copy of the array (immutable), enumerate that and remove the objects (not by index) from the original while enumerating.
The traditional for loop could be replaced with a simple while loop, useful if you also need to perform some other operations on each element prior to removal.
var index = array.count-1
while index >= 0 {
let element = array[index]
//any operations on element
array.remove(at: index)
index -= 1
}
I recommend to set elements to nil during enumeration, and after completing remove all empty elements using arrays filter() method.
Just to add, if you have multiple arrays and each element in index N of array A is related to the index N of array B, then you can still use the method reversing the enumerated array (like the past answers). But remember that when accessing and deleting the elements of the other arrays, no need to reverse them.
Like so, (one can copy and paste this on Playground)
var a = ["a", "b", "c", "d"]
var b = [1, 2, 3, 4]
var c = ["!", "#", "#", "$"]
// remove c, 3, #
for (index, ch) in a.enumerated().reversed() {
print("CH: \(ch). INDEX: \(index) | b: \(b[index]) | c: \(c[index])")
if ch == "c" {
a.remove(at: index)
b.remove(at: index)
c.remove(at: index)
}
}
print("-----")
print(a) // ["a", "b", "d"]
print(b) // [1, 2, 4]
print(c) // ["!", "#", "$"]
I have these arrays of arrays :
array0 = [["1"], ["2"], ["3"],…]
array1 = [["a"], ["b"], ["c"],…]
array2 = [["pap"], ["pop"], ["pip"],…]
I want to push every element in every array, to the first array so the output should be:
output = [["1", "a", "pap"], ["2", "b", "pop"], ["3", "c" ,"pip"]]
First do flatten on each array to get single dimension array. Then use zip to get 2-dimension array each having 3-elements
array0.flatten.zip(array1.flatten, array2.flatten)
Shorter way of doing the same: (Solution given by #Ivaylo Strandjev)
array0.zip(array1, array2).map(&:flatten)
Try this:
array0.zip(array1).zip(array2).map(&:flatten)
Also you can do it with a single zip:
array0.zip(array1, array2).map(&:flatten)