let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sort(backwards)
print(reversed)
var ascending = names.sort({ (s1: String, s2: String) -> Bool in
return s1 < s2
})
print(ascending)
let sortAscending = { (s1: String, s2: String) -> Bool in
return s1 < s2
}
ascending = names.sort(sortAscending)
I am suppose to sort this code according to the number of characters they have from the most to the least. For example, Daniella has 8 characters so she will be fist in the list. The output I am suppose to get is
["Daniella", "Barry", "Chris", "Alex", "Ewa"]
Try this:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let sortedNames = names.sorted { $0.characters.count > $1.characters.count }
print(sortedNames)
Should I sort or should I sorted? Use sort to order the original array in-place, if declared as a var; if let your code won't even compile. Use sorted to leave your original array alone and return a new, properly sorted array; works on let and var.
Related
I have a problem statement to write an in-place function to eliminate the adjacent duplicates in a string slice.
I came up with the following code
func main() {
tempData := []string{"abc", "abc", "abc", "def", "def", "ghi"}
removeAdjacentDuplicates(tempData)
fmt.Println(tempData)
}
func removeAdjacentDuplicates(data []string) {
for j := 1; j < len(data); {
if data[j-1] == data[j] {
data = append(data[:j], data[j+1:]...)
} else {
j++
}
}
fmt.Println(data)
}
The output is following
[abc def ghi]
[abc def ghi ghi ghi ghi]
My doubt is, if in the function, slice is modified, then in the calling function, why isn't the slice giving correct results?
Also, any article to understand the slices (and the underlying array) much better would be very helpful.
The func removeAdjacentDuplicate takes the slice "as if" it is a reference to tempData
The capacity and length of tempData in the main() stays the same for the lifetime
of the program
In the removeAdjacentDuplicate func each time a dupe is found the final value of "ghi" is moved from the end to the end - 1. So in the memory at the end of the
slice there are repeated "ghi"
When the control returns to the main, the program prints out the now modified
slice tempData. Because it was passed in a similar way to a reference to the
function it is this memory that was modified. The function call did not make a copy of the memory
You can see this behaviour by looking at the cap() and len() as the program runs
package main
import (
"fmt"
)
func main() {
tempData := []string{"abc", "abc", "abc", "def", "def", "ghi"}
removeAdjacentDuplicates(tempData)
fmt.Println(tempData,cap(tempData),len(tempData))
}
func removeAdjacentDuplicates(data []string) {
for j := 1; j < len(data); {
if data[j-1] == data[j] {
data = append(data[:j], data[j+1:]...)
fmt.Println(data,cap(data),len(data))
} else {
j++
}
}
fmt.Println(data, cap(data),len(data))
}
In your code, removeAdjacentDuplicates wants to mutate the slcie passed in argument. This is not really possible.
This function should return the new slice, just like append does.
func removeAdjacentDuplicates(data []string) []string{
for j := 1; j < len(data); {
if data[j-1] == data[j] {
data = append(data[:j], data[j+1:]...)
} else {
j++
}
}
return data
}
If you really want to mutate the argument, it is possible but you need to pass a pointer to a slice *[]string
Go 1.18 and above
You can use slices.Compact exactly for this.
Compact replaces consecutive runs of equal elements with a single copy. This is like the uniq command found on Unix. Compact modifies the contents of the slice s; it does not create a new slice.
func main() {
data := []string{"abc", "abc", "abc", "def", "def", "ghi"}
data = slices.Compact(data)
fmt.Println(data) // [abc def ghi]
}
The package is golang.org/x/exp/slices, which is still experimental. If you don't want to import an exp package, you can copy the source:
func Compact[S ~[]E, E comparable](s S) S {
if len(s) == 0 {
return s
}
i := 1
last := s[0]
for _, v := range s[1:] {
if v != last {
s[i] = v
i++
last = v
}
}
return s[:i]
}
If the slice's elements aren't comparable, use slices.CompactFunc which works the same but takes also a comparator function.
Try this function:
func deleteAdjacentDuplicate(slice []string) []string {
for i := 1; i < len(slice); i++ {
if slice[i-1] == slice[i] {
copy(slice[i:], slice[i+1:]) //copy [4] where there is [3, 4] => [4, 4]
slice = slice[:len(slice)-1] //removes last element
i-- //avoid advancing counter
}
}
return slice
}
Here is my homework:
We'll say that 2 strings "match" if they are non-empty and their first chars are the same. Loop over and then return the given array of non-empty strings as follows: if a string matches an earlier string in the array, swap the 2 strings in the array. When a position in the array has been swapped, it no longer matches anything. Using a map, this can be solved making just one pass over the array.
allSwap(["ab", "ac"]) → ["ac", "ab"]
allSwap(["ax", "bx", "cx", "cy", "by", "ay", "aaa", "azz"]) → ["ay", "by", "cy", "cx", "bx", "ax", "azz", "aaa"]
allSwap(["ax", "bx", "ay", "by", "ai", "aj", "bx", "by"]) → ["ay", "by", "ax", "bx", "aj", "ai", "by", "bx"]
In the map, store the first letter as the key, and the most recent index of the key as the value. When a letter doesn't exist in the map, add it to the map. When a letter already exists in the map, remove it from the map and swap with that index.
/**
* Swaps strings in the array that have the same first letter,
* reading left to right. Once a string has been swapped,
* it will not be swapped again. The input array will be mutated.
*
* #param strings the strings to perform swaps from
* #return the strings after swapping
*/
public static String[] allSwap(final String[] strings) {
// map of first characters, and the index where they were last seen
final Map<Character, Integer> potentialSwap = new HashMap<>();
for (int thisIndex = 0; thisIndex < strings.length; thisIndex++) {
if (strings[thisIndex].isEmpty()) {
continue; // skip empty strings
}
final Character firstChar = strings[thisIndex].charAt(0); // box charAt(0)
// remove firstChar from the map. If it's not found, returns null
final Integer potentialIndex = potentialSwap.remove(firstChar);
if (potentialIndex != null) {
final int thatIndex = potentialIndex; // unbox potentialIndex
// swap values at thisIndex and thatIndex
final String temp = strings[thatIndex];
strings[thatIndex] = strings[thisIndex];
strings[thisIndex] = temp;
} else {
// save the index for possible swapping later
potentialSwap.put(firstChar, thisIndex); // box thisIndex
}
}
return strings;
}
Ideone Demo
public String[] allSwap(String[] strings) {
// map of first characters, and the index where they were last seen
Map<Character, Integer> map = new HashMap();
for (int i = 0; i < strings.length; i++) {
char firstChar = strings[i].charAt(0);
// box charAt(0)
// remove firstChar from the map. If it's not found, returns null
Integer removedIndex = map.remove(firstChar);
if (removedIndex != null) {
int j = removedIndex;
// unbox potentialIndex
// swap values at thisIndex and thatIndex
String temp = strings[j];
strings[j] = strings[i];
strings[i] = temp;
} else {
// save the index for possible swapping later
map.put(firstChar, i);
}
}
return strings;
}
This can also work !
public String[] allSwap(String[] strings) {
Map<String,Integer> map = new HashMap<String,Integer>();
for(int i=0;i<strings.length;i++){
if(!map.containsKey(strings[i].substring(0,1))){
map.put(strings[i].substring(0,1),i);
}
else{
String temp = strings[map.get(strings[i].substring(0,1))];
strings[map.get(strings[i].substring(0,1))] = strings[i];
strings[i] = temp;
map.remove(strings[i].substring(0,1));
}
}
return strings;
}
I am trying to generate 5 random substrings of six characters each from the alphabet. For example: ABCDEF, RSTUVW, UVWXYZ, etc. These substrings can be duplicates, so generating ABCDEF twice is a not a problem.
When I have these 5 substrings, I want to generate five arrays containing three characters. One of these characters should be the last letter of the substring and the other two letters should be two random unique letters from the entire alphabet.
Example:
Get five random substrings:
[ABCDEF], [RSTUVW], [CDEFGH], [LMNOPQ], [UVWXYZ]
For [ABCDEF] the system could generate [F, H, S] and for [RSTUVW] it could generate [K, Q, W]. As you can see, the three-character arrays always contain the last letter of its substring and two other randomised unique letters.
The above is part of a game for kids to practice the order of the alphabet. In order to generate possible answers I actually need small sets of characters to assign to buttons.
What do you think is the best way to approach this?
Thanks to #vacawama here's a possible solution.
1. Create the alphabet
let alphabet = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
2. Create the 6 "sequences"
let sequences = (0..<5).map { _ -> String in
let startIndex = Int(arc4random_uniform(UInt32(alphabet.count - 5)))
let endIndex = startIndex + 5
return String(alphabet[startIndex...endIndex])
}
["PQRSTU", "DEFGHI", "JKLMNO", "CDEFGH", "KLMNOP"]
3. Get the last char for each sequence
let lastChars = sequences.flatMap { $0.characters.last }
["U", "I", "O", "H", "P"]
4. Build 5 elms
Here's the code snippet
let elms = lastChars.map { char0 -> String in
var tempAlphabet = alphabet
tempAlphabet.removeAtIndex(tempAlphabet.indexOf(char0)!)
let index1 = Int(arc4random_uniform(UInt32(tempAlphabet.count)))
let char1 = tempAlphabet.removeAtIndex(index1)
let index2 = Int(arc4random_uniform(UInt32(tempAlphabet.count)))
let char2 = tempAlphabet[index2]
return String(Array(Set<Character>([char0, char1, char2])))
}
["DUN", "ZIQ", "ROP", "HSW", "PGS"]
Update
Here's another solution to fix the problem hightligted by #dfri in the comments below. The following code snipped could replace the previous bullet 4.
extension Array {
mutating func removeRandom() -> Element {
let index = Int(arc4random_uniform(UInt32(count)))
return removeAtIndex(index)
}
}
var availableChars = Array(Set(alphabet).subtract(lastChars))
let elms = lastChars.map { String([$0, availableChars.removeRandom(), availableChars.removeRandom()]) }
Random substrings:
func randomString(length: Int) -> String {
let charactersString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let n = UInt32(charactersString.characters.count)
var out = ""
for _ in 0..<length {
let index = charactersString.startIndex.advancedBy(Int(arc4random_uniform(n)))
out.append(charactersString[index])
}
return out
}
Get 5 Strings:
func createSubstrings() -> [String] {
var array = [String]()
for _ in 0...5 {
array.append(self.randomString(6))
}
return array
}
Last array:
func createFinalStrings() -> [String] {
let substrings = createSubstrings()
var finalStrings = [String]()
let lastChars = substrings.flatMap { $0.characters.last }
for _ in 0...5 {
var string = ""
while string.characters.count < 3 {
let index = Int(arc4random_uniform(5))
let lastChar = lastChars[index]
if string.rangeOfString(String(lastChar)) == nil{
string = string + String(lastChar)
}
}
finalStrings.append(string)
}
return finalStrings
}
i have 2 different arrays called: criptedChar and alphabet. I need to check the first character in criptedChar (so "criptedchar[0]) and check a correspondence in alphabet.
For exemple:
criptedChar // ["d","e","c","b"]
alphabet // ["a","b","c" and so on]
I want to take d from criptedChar[0] and check if there's a "d" in all alphabet and then save the position of "d" in the second array.
I also need to increment the number inside the parenthesis of criptedChar. I'll take the number from the user.
Can you please help me? Thank you!
func decript() {
var criptedText = incriptedText.text! //get text from uiTextField
var criptedChar = Array<Character>(criptedText.characters) //from text to char & all in array :D
var alfabeto: Array<Character> = ["a","b", "c", "d", "e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
var capacityCriptedCharArray = criptedChar.capacity
for (var i = 0; i < 26; i++) {
if criptedChar[0] == alfabeto[i] {
decriptedText.text = decriptedText.text! + "\(newLettersFromSecondViewController[i])"
}
}
for (var i = 0; i < 26; i++) {
if criptedChar[1] == alfabeto[i] {
decriptedText.text = decriptedText.text! + "\(newLettersFromSecondViewController[i])"
}
}
for (var i = 0; i < 26; i++) {
if criptedChar[2] == alfabeto[i] {
decriptedText.text = decriptedText.text! + "\(newLettersFromSecondViewController[i])"
}
}
}
This code works, but it's dumb and i have no control of the user input
If I understand your question correctly, you are looking for
something like this (explanations inline):
// Start with your crypted text, and an empty string for the result:
let cryptedText = "mifpyx"
var decryptedText = ""
// Two character arrays (of equal length):
let alphabet = Array("abcdefghijklmnopqrstuvwxyz".characters)
let newLetters = Array("ghijklmnopqrstuvwxyzabcdef".characters)
// For each character in the input string:
for c in cryptedText.characters {
// Check if `c` is contained in the `alphabet` array:
if let index = alphabet.indexOf(c) {
// Yes, it is, at position `index`!
// Append corresponding character from second array to result:
decryptedText.append(newLetters[index])
}
}
print(decryptedText) // solved
Alternatively, you can create a lookup-dictionary from the
two arrays:
var mapping = [ Character : Character ]()
zip(alphabet, newLetters).forEach {
mapping[$0] = $1
}
and then map each character from the input through that
dictionary:
let decryptedText = Array(cryptedText.characters
.map { mapping[$0] }
.flatMap { $0 }
)
(Here flatMap is used to filter-out the nils from characters which are not present in the input array.)
Here are examples of simple cypher logic that you can probably adapt to your application:
let readableText = "the quick brown fox jumped over the lazy dog"
// letters: letters in readable text that will be encoded
// cypher : corresponding encoded letters
//
// note: letters and cypher must have the same number of elements
let letters:[Character] = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
let cypher:[Character] = ["o","p","q","r","a","b","c","d","e","f","g","h","i","u","v","w","x","y","z","j","k","l","m","n","s","t"]
// build a mapping disctionary from readable to encoded
var encode:[Character:Character] = [:]
for (index, letter) in letters.enumerate() { encode[letter] = cypher[index] }
// encrypt the readble text gives: "jda xkeqg pyvmu bvn fkiwar vlay jda hots rvc"
let cryptedText = String(readableText.characters.map({ encode[$0] ?? $0 }))
// build a mapping disctionary from encoded to readable
var decode:[Character:Character] = [:]
for (index, letter) in cypher.enumerate() { decode[letter] = letters[index] }
// decrypted the encrypted text gives: "the quick brown fox jumped over the lazy dog"
let decryptedText = String(cryptedText.characters.map({ decode[$0] ?? $0 }))
I need to find the most common (modal) elements in an array.
The simplest way I could think of was to set variables for each unique element, and assign a count variable for each one, which increases every time it is recorded in a for loop which runs through the array.
Unfortunately the size of the array is unknown and will be very large, so this method is useless.
I have come across a similar question in Objective-C that uses an NSCountedSet method to rank the array elements. Unfortunately I am very new to programming, and could only translate the first line into Swift.
The suggested method is as follows:
var yourArray: NSArray! // My swift translation
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:yourArray];
NSMutableDictionary *dict=[NSMutableDictionary new];
for (id obj in set) {
[dict setObject:[NSNumber numberWithInteger:[set countForObject:obj]]
forKey:obj]; //key is date
}
NSLog(#"Dict : %#", dict);
NSMutableArray *top3=[[NSMutableArray alloc]initWithCapacity:3];
//which dict obj is = max
if (dict.count>=3) {
while (top3.count<3) {
NSInteger max = [[[dict allValues] valueForKeyPath:#"#max.intValue"] intValue];
for (id obj in set) {
if (max == [dict[obj] integerValue]) {
NSLog(#"--> %#",obj);
[top3 addObject:obj];
[dict removeObjectForKey:obj];
}
}
}
}
NSLog(#"top 3 = %#", top3);
In my program I will need to find the top five place names in an array.
edit: now with Swift 2.0 below
Not the most efficient of solutions but a simple one:
let a = [1,1,2,3,1,7,4,6,7,2]
var frequency: [Int:Int] = [:]
for x in a {
// set frequency to the current count of this element + 1
frequency[x] = (frequency[x] ?? 0) + 1
}
let descending = sorted(frequency) { $0.1 > $1.1 }
descending now consists of an array of pairs: the value and the frequency,
sorted most frequent first. So the “top 5” would be the first 5 entries
(assuming there were 5 or more distinct values). It shouldn't matter how big the source array is.
Here's a generic function version that would work on any sequence:
func frequencies
<S: SequenceType where S.Generator.Element: Hashable>
(source: S) -> [(S.Generator.Element,Int)] {
var frequency: [S.Generator.Element:Int] = [:]
for x in source {
frequency[x] = (frequency[x] ?? 0) + 1
}
return sorted(frequency) { $0.1 > $1.1 }
}
frequencies(a)
For Swift 2.0, you can adapt the function to be a protocol extension:
extension SequenceType where Generator.Element: Hashable {
func frequencies() -> [(Generator.Element,Int)] {
var frequency: [Generator.Element:Int] = [:]
for x in self {
frequency[x] = (frequency[x] ?? 0) + 1
}
return frequency.sort { $0.1 > $1.1 }
}
}
a.frequencies()
For Swift 3.0:
extension Sequence where Self.Iterator.Element: Hashable {
func frequencies() -> [(Self.Iterator.Element,Int)] {
var frequency: [Self.Iterator.Element:Int] = [:]
for x in self {
frequency[x] = (frequency[x] ?? 0) + 1
}
return frequency.sorted { $0.1 > $1.1 }
}
}
For XCode 7.1 the solution is.
// Array of elements
let a = [7,3,2,1,4,6,8,9,5,3,0,7,2,7]
// Create a key for elements and their frequency
var times: [Int: Int] = [:]
// Iterate over the dictionary
for b in a {
// Every time there is a repeat value add one to that key
times[b] = (times[b] ?? 0) + 1
}
// This is for sorting the values
let descending = times.sort({$0.1 > $1.1})
// For sorting the keys the code would be
// let descending = times.sort({$0.0 > $1.0})
// Do whatever you want with sorted array
print(descending)
Same as Airspeed Velocity, using a reduce instead of for-in:
extension Sequence where Self.Iterator.Element: Hashable {
func frequencies() -> [(Self.Iterator.Element, Int)] {
return reduce([:]) {
var frequencies = $0
frequencies[$1] = (frequencies[$1] ?? 0) + 1
return frequencies
}.sorted { $0.1 > $1.1 }
}
}
But please note that, here, using reduce with a struct is not as efficient as a for-in because of the struct copy cost. So you will generally prefer the for-in way of doing it.
[edit: gosh, the article is by the same guy as the top answer!]