I dont know how doint that. I try find replacement obj method
indexOfObject on the swift but not find
i have array = [1,3,6,2,3] and input in array value 3, i am need find repeating value with min index from array. Answer with index 1. How do it?
You have to loop through the entire collection, for each item, see if we had seen this before. And as we are doing that, let’s keep track of the index of the first item that has been repeated somewhere in the collection, to see if this is the first repeated item or not:
extension Collection where Element: Hashable {
func indexOfFirstRepeated() -> Index? {
var result: Index? // the index of the first item repeated anywhere else in the collection
var firstOccurrences: [Element: Index] = [:] // dictionary to keep track of the first item that every element was first encountered in the collection
for (index, element) in zip(indices, self) {
if let firstOccurrence = firstOccurrences[element] { // find previous occurrence of this value, if any
if let previousLowestIndex = result { // if we found this element before, let's see if we had already found another repeated element
if firstOccurrence < previousLowestIndex { // if so, let’s see if the first occurrence of this element occurred before the first occurrence of the previously discovered repeated element
result = firstOccurrence
}
} else { // otherwise, no prior repeated element found, so this is our first repeated element found thus far
result = firstOccurrence
}
} else {
firstOccurrences[element] = index // if we got here, this is the first time we've seen this element, so record the index of this first occurrence
}
}
return result
}
}
Thus:
let array = [9,8,7,1,3,6,2,3,1]
if let index = array.indexOfFirstRepeated() {
print(index) // 3
}
Now, obviously, as we iterate through this array, the value 3 is the first value that we will see repeated, but that doesn’t matter, because the repeated value 1 will be found at the very end of the array, and 1’s first index is lower than 3’s first index.
Two observations on the above:
I made it generic, so that it works on any hashable type, e.g.:
let array = ["bill", "sam", "susan", "sam", "bill"]
if let index = array.indexOfFirstRepeated() {
print(index)
} else {
print("not found")
}
I made this a Collection extension (rather than an Array extension) so that it would work on other collection types (e.g. array slices, etc.). You can make it an Array extension, just as easily, but we prefer to use the most abstract type that is convenient, to make it as flexible as possible.
This is basically a riff on uniqued.
import Algorithms
public extension BidirectionalCollection where Element: Hashable {
var firstDuplicate: (index: Index, element: Element)? {
var set: Set<Element> = []
return indexed().reversed().reduce(into: nil) {
if !set.insert($1.element).inserted {
$0 = $1
}
}
}
}
You can get the last duplicate by not reversing before reducing.
Assume you have an array of n integers, with a[i] = i, except that a[1] = a[2] = 1, and I may or may not have have changed a[i] = 0 for some i >= 2.
The smallest index of a duplicate element is either 0 or 1. To find out which you have to find the i >= 2 with a[i] = 0 or find that no such i exists. So you have to visit all array elements.
Related
Is there a way to grab the first non-zero element from an array of numbers?
I have an Array with many zeros at the beginning and I need only the first item that is not a zero.
For example:
let array = [0,0,0,0,25,53,21,77]
based on the above, the result should be 25.
What is the good way to achieve it?
You could get it like this:
let array = [0,0,0,0,25,53,21,77]
let firstNonZero = array.first { element -> Bool in
return element != 0
}
Or as a shorter version:
let firstNonZero = array.first(where: { $0 != 0 })
Note that firstNonZero would be an optional Int, so in case of array contains only zeros, firstNonZero would be nil.
Aside bar note: If you were wondering why to use first(where:) instead of filter(_:).first, you could check this question:
What is the difference between filter(_:).first and first(where:)?
I am currently having a big issue sorting my Data alphabetically in a 2D array. I'm going to try to give you every detail to be as clear as possible.
Currently, I am fetching my contacts with the CNContactStore. This all works fine. I am able to retrieve all the data I want out of my contacts.
Now, I created the following struct:
struct FavoritableContact {
let contact: CNContact
var hasFavorited: Bool
}
With this, I declared and initialized the following array:
var favoritableContacts = [FavoritableContact]()
Once I retrieved my contacts, I simply appended them to favoritableContacts;
try store.enumerateContacts(with: request, usingBlock: { (contact, stopPointerIfYouWantToStopEnumerating) in
favoritableContacts.append(FavoritableContact(contact: contact, hasFavorited: false))
})
To sort them in alphabetical order in the same array, I simply did the following:
var sortedContacts = favoritableContacts.sorted { $0.contact.familyName < $1.contact.familyName }
Now if possible, I want to create the following 2D array,
var 2D = [
[FavoritableContact] //"A"
[FavoritableContact], //"B"
[FavoritableContact], //"C"
[FavoritableContact], //"D"
...
]
I am just not sure how to take my sortedContacts array and separate alphabetically.
I am very new here, If I forgot something, or I didn't do somethign right please let me know.
As was pointed out in the comments, a dictionary with first letters as keys is probably the better way to go as it is much easier to access, though perhaps you have a reason for wanting to use a 2d array instead. To achieve that you could do something like this:
//Create an empty array filled with 26 arrays of FavorableContact
var array2d = Array<[FavoritableContact]>(repeating: [FavoritableContact](), count: 26)
//Find the ascii value for "A" to use as your base
let aAscii = Int("A".unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0]) //This returns 65, btw, so you could also just hardcode
//Go through your original array, find the first letter of each contact, and append to the correct array
favoritableContacts.forEach { (contact) in
//Get the ascii value for the first letter
let firstLetter = Int(contact.contact.familyName.prefix(1).uppercased().unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])
//Append to the array for this letter by subtracting the ascii value for "A" from the ascii value for the uppercased version of this letter.
array2d[firstLetter - aAscii].append(contact)
}
This is not the cleanest thing in the world, and it assumes standard English language alphabet with no diacritics, symbols, numbers or anything else. Assuming that is true it gets the job done.
Could use something like this.
var contactsLeftToSort : [FavoritableContact] = []
var doubleArray : [[FavoritableContact]?] = [[FavoritableContact]?]()
var index : Int = 0
for char in "ABCDEFGHIJKLMNOPQRSTUV" {
doubleArray.append(nil)
var i = 0
while i < contactsLeftToSort.count {
let contact = contactsLeftToSort[i]
if contact.name.first == char {
doubleArray[index] == nil ? doubleArray[index] = [contact] : doubleArray[index]!.append(contact)
contactsLeftToSort.remove(at: i)
}
//assuming original list is alphabetized.. if not, delete this line.
if contact.name.first! > char { break }
i += 1
}
index += 1
}
As I wrote in the comments above, I think you can achieve this in a much more elegant way by using a dictionary instead of an array.
SWIFT 4
let sortedContacts: [FavoritableContact] = ... // An array of FavoritableContact objects, they should be sorted
let groupedContacts = Dictionary(grouping: contacts, by { $0.familyName.first! })
You now have a dictionary of all your contacts where the keys are the alphabetical letters (ie. A-Z) and the values are arrays of sorted FavoritableContact objects (assuming you sorted the big array of FavoritableContacts before creating the dictionary).
If you wanted to use this as the datasource for your tableview, you would make the number of sections all the possible first letters of family names. For the number of rows in each section, you return the count of the array for the key like so:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
let letterForSection = letterForSection() // Custom method to get the section of the letter
return contactsDict[letterForSection].count
}
The rest of the datasource methods would work in a similar way.
Man, all of these answers are really over-complicating this. All you need is something along the lines of:
let groupedContacts = Dictionary(grouping: contacts, by: { $0.contact.firstName.first! })
for initial, contacts in groupedContacts.lazy.sorted().{ $0.key < $1.key} {
print("#################", initial)
contacts.forEach{ print($0) }
}
I currently have 2 arrays, one called rewardsArray and one called expiredRewardsArray. The rewardsArray I'm getting from an api thats fetched every time I connect. The expiredRewardsArray I save locally. What I'm trying to do is in the viewDidLoad, after I retrieve the rewardsArray data I want to compare it to the expiredRewardsArray's data. If there is a match I want to remove the item from the rewardsArray. This is what I have so far but it never goes inside the "if let" brackets so it's not removing the item from the rewardsArray:
func rewardsMinusExpired () {
expiredRewardsArray = rewardManager.getExpiredRewards()
for expiredReward in expiredRewardsArray {
if let ex = rewardsArray.indexOf(expiredReward){
print("Expired Reward to be removed: \(ex)")
rewardsArray.removeAtIndex(ex)
rewardsTableView.reloadData()
}
}
}
Each item in the array has an id, I use that to see if the item is in the expiredRewardsArray:
for expiredReward in expiredRewardsArray {
print("This is an expired reward: \(expiredReward.id)")
}
Here's a solution that uses Set arithmetic. As a consequence, it's really fast, but it will not preserve the ordering of elements, and it will clobber any duplicates. It runs in linear time (O(n)), whereas a naive approach would be O(n^2).
let unexpiredRewards = Set(rewardsArray).subtract(Set(ExpiredRewards))
for item in expiredRewardsArray {
if let index = rewardsArray.index(of: item) {
//found the item
rewardsArray.remove(at: index)
}
}
This will find the item and delete it from the rewardsArray
UPDATE:
You say that each item has an id. If with the code above the if let block is never called than you can be sure that the items aren't actually the same or you don't have any equal items.
for itemOne in expiredRewardsArray {
for itemTwo in rewardsArray {
if itemOne.id == itemTwo.id {
rewardsArray.remove(at: index)
}
}
}
Not very performant but it does its job and keeps the order
You should really use swift method filter in cases like this. This can be easily solved as this,
func rewardMinusExpired() {
let notExpiredRewards = rewardsArray.filter { a in
return !expiredRewardArray.contains(a)
}
}
If expiredRewardsArray and rewardsArray are arrays of objects, then you'll need to compare the elements using their id property to compare them, rather than indexOf(_:). The reason for this is that objects are reference types, so you can have two different objects with identical properties, but if they're not the same object, indexOf(_:) will treat them as separate entities.
Try this code instead:
func rewardsMinusExpired() {
expiredRewardsArray = rewardManager.getExpiredRewards()
rewardsArray = rewardsArray.filter { reward in
let isExpired = expiredRewardsArray.contains { expiredReward in
return expiredReward.id == reward.id
}
return !isExpired
}
}
I am trying to shuffle my arrays using the following extension:
extension Array {
var shuffled: [Element] {
guard count > 1 else { return self }
var elements = self
for index in indices {
let newIndex = Int(arc4random_uniform(UInt32(elements.count - index))) + index
guard index != newIndex else { continue }
swap(&elements[index], &elements[newIndex])
}
return elements
}
}
I am then using this code to shuffle and get the first 20 items on my plist:
var questionsArray = [myPlistArray?.shuffled]
let shuffledQuestions = questionsArray.shuffle() // instance member cannot be used on type 'myViewController'
let first20 = questionsArray.prefix(20)
However, I always get this error:
// instance member cannot be used on type ‘myViewController’
Does anyone know how to do it right?
Or does anyone know how can I shuffle pList items and get the first 20 items?
My questions is quite the same with this one: How to return first 5 objects of Array in Swift?, however, my data is in a plist dictionary file...
Anyways, my pList file is this: myPlistArray.plist
Your 3 lines that are supposed to shuffle the array is jumbled and doesn't make much sense.
var questionsArray = [myPlistArray?.shuffled]
That line creates an array containing a single element, a shuffled version of myPlistArray. So you have an array within an array, which doesn't make sense.
let shuffledQuestions = questionsArray.shuffle() // instance member cannot be used on type 'myViewController'
That line tries to call a method shuffle() (not shuffled, shuffle, which doesn't exist) on the screwy shuffledQuestions array-within-array you created in the previous line.
let first20 = questionsArray.prefix(20)
That line would actually make sense if your first 2 lines were well formed.
You just want this:
let questionsArray = myPlistArray?.shuffled //Create a shuffled version
let first20 = questionsArray.prefix(20) //take the first 20 items.
That code should work assuming that myPlistArray is an optional array.
So I have two arrays a name array and a values array they are a string and double respectively. I want to be able for a user to type in a textfield and to iterate though the name array until there is a match then output the value that has the same number as the name to be outputted
this is the code i have:
for(var i = 0; i<name.count; i++){
if name[i] == typeFood{
yieldOutput == percent[i]
}
}
First, use find to locate the location of the value in the name array, then use the returned index to look up the percent:
if let idx = find(name, typeFood) {
yieldOutput = percent[idx]
}
You can also combine the two actions together with map to declare an output variable using let:
if let foodPercentage = find(name, typeFood).map({ percent[$0] }) {
// foodPercentage will have a value here
}
else {
// not-found logic here
}