I've been learning Swift 3 syntax lately and I thought that a good first project would be a Vigenère Cipher. So I've begun to create a script for it in Playground.
The issue is that I keep getting an error when I call the method and I know what the mistake is, it has to do with how I am calling my Dictionary value and the fact that I am unwrapping it, But I don't know what else to do about it. Any ideas?
import Foundation
let alphabet: [String: Int] = ["a": 0, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, "g": 6, "h": 7, "i": 8,
"j": 9, "k": 10, "l": 11, "m": 12, "n": 13, "o": 14, "p": 15, "q": 16,
"r": 17, "s": 18,"t": 19, "u": 20, "v": 21, "w": 22, "x": 23, "y": 24, "z": 25]
let alphabet2: [Int: String] = [0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 7: "h", 8: "i",
9: "j", 10: "k", 11: "l", 12: "m", 13: "n", 14: "o", 15: "p", 16: "q",
17: "r", 18: "s", 19: "t", 20: "u", 21: "v", 22: "w", 23: "x", 24: "y", 25: "z"]
var mess = "I once saw a big mosquito"
var key = "pass"
func cipher(message: String, key: String) -> String{
var code: [Int] = [] // will hold the encripted code
// removes whietspace from message and key
let trimmedMessage = message.trimmingCharacters(in: NSCharacterSet.whitespaces)
let trimmedKey = key.trimmingCharacters(in: NSCharacterSet.whitespaces)
// Sets the key the same size as the message
let paddedTrimmedKey = trimmedKey.padding(toLength: message.characters.count, withPad: trimmedKey, startingAt: 0)
// separates the message and key into individual characters
let charTrimmedMessage = Array(trimmedMessage.characters)
let charPaddedTrimmedKey = Array(paddedTrimmedKey.characters)
// Compare the values in the key to the message and scrambles the message.
var i = 0
for chr in charTrimmedMessage{
code.append((alphabet[String(chr)]! + alphabet[String(charPaddedTrimmedKey[i])]!) % 26) // <- I think the error comes from this line. Maybe the exclamation marks?
i += 1
}
var cyphr: String = "" // this will hold the return String
// I think I could have used some sort of "join" function here.
for number in code{
cyphr = cyphr + alphabet2[number + 1]!
}
return cyphr
}
cipher(message: mess, key: key) // <--- this returns an error, no clue why. The code works and compiles great.
I get this error:
If you could let me know any pointer on how to improve my code to avoid things like this even better.
Your alphabet dictionary does not have lookup values for the letters "I", " " which are contained in your plain text. The cipher function fails because of this (since it is force unwrapping an optional which is nil)
If you initialize the mess variable, you will get the cipher to work
var mess = "ioncesawabigmosquito"
cipher(...) -> "ypgvuttpqcbzcpljkjmh"
Either
update the alphabet lookup dictionary. E.g. add ' ' as a valid input, update alphabet2 to provide reverse lookup, and change the mod factor from 26 to 27 (to account for new ' ' character)
OR
validate your input beforehand. (trim spaces, replace spaces, convert uppercase to lowercase / strip characters not in valid range)
To trim your input to only include valid letters in the alphabet, you could try:
let validSet = CharacterSet.init(charactersIn: alphabet.keys.joined())
var messTrimmed = mess.trimmingCharacters(in: validSet.inverted)
Note that doing this mean losing information from the original message
Another bug:
The line cyphr = cyphr + alphabet2[number+1]! should be cyphr = cyphr + alphabet2[number]!.
It's not correct to add 1 to number since the values in the code array are computed mod 26, and the max key value in alphabet is 25. It will cause an exception when force unwrapping to a non-existent key.
E.g. try cipher(message: "ypgvuttpqcbzcpljkjmh", key: "pass") to make it fail.
Footnote
As a complete aside, here's a variant of the cipher function (I have not sanitized the inputs; just showing how the code could be written in a functional way)
func cipher2(message: String, key: String) -> String {
return
message
.characters
.enumerated()
.map { ($0, String($1)) }
.map {
(
alphabet[$1]!
+ alphabet[String(key[key.index(key.startIndex, offsetBy: ($0 % key.characters.count))])]!
) % 26
}
.map { alphabet2[$0]! }
.joined()
}
Related
I use protoc to generate proto file to .go file .
In Protocol Buffers, I want to use the enums value with self defined value.
enum Region {
North_America = 0; // I want to set it to be "North America" use space replace the _
Southr_America =1;
}
The generated golang to be
const (
Region_North_America Region = 0
Region_Southr_America Region = 1
)
var Region_name = map[int32]string{
0: "North_America",
1: "Southr_America",
}
var Region_value = map[string]int32{
"North_America": 0,
"Southr_America": 1,
}
How can I give some tag for enum, so that the generated
var Region_name = map[int32]string{
0: "North America",
1: "Southr America",
}
var Region_value = map[string]int32{
"North America": 0,
"Southr America": 1,
}
This cannot be done; if an element is added or removed, the entire structure will be broken. Therefore, you need to explicitly specify identifiers
... = 0; required
I using linq.js and i want replace single quotes when searching data.
This my code.
var list = [
{ a: "50", b: 4, c: 1 },
{ a: "60", b: 3, c: 7 },
{ a: "'540'", b: 3, c: 3 }
];
var val = "'540'";
val = val.replace(/'/g, "'");
var res = Enumerable.From(list).Where("($.a).replace(\"'\",\"'\")=='" + val + "'").ToArray();
If there is only 1 single quote in the data, it works.
But if there are 2 single quote in it, it can't search.
Oh, linq.js the same code in javascript.
I have to use regex to replace:
It is ok , if i change to :
var res = Enumerable.From(list).Where("($.a).replace(\/'\/g,\"'\")=='" + val + "'").ToArray();
How can you convert a range in Java (either using java.util.stream.LongStream or java.util.stream.IntStream) to a delimited string in Java?
I have tried:
String str = LongStream.range(16, 30)
.boxed()
.map(String::valueOf)
.collect(Collectors.joining(","));
System.out.println(str);
This prints:
16,17,18,19,20,21,22,23,24,25,26,27,28,29
The same can be used with IntStream. Is there a more convenient conversion of a range to a delimited string?
Seriously, just for the fun of it. Using guava:
String result = ContiguousSet.create(
Range.closedOpen(16, 31), DiscreteDomain.integers())
.asList()
.toString();
Or
String result = String.join(",",
IntStream.rangeClosed(16, 30).mapToObj(String::valueOf).toArray(String[]::new));
Or:
String result = String.join(",",
() -> IntStream.rangeClosed(16, 31).mapToObj(x -> (CharSequence) String.valueOf(x)).iterator());
Or (seems like I got carried away a bit with this):
String result = IntStream.rangeClosed(16, 31)
.boxed()
.collect(
Collector.of(
() -> new Object() {
StringBuilder sb = new StringBuilder();
},
(obj, i) -> obj.sb.append(i).append(",")
,
(left, right) -> {
left.sb.append(right.sb.toString());
return left;
},
x -> {
x.sb.setLength(x.sb.length() - 1);
return x.sb.toString();
})
);
And after Holger's good points, here is even a simpler version:
StringBuilder sb = IntStream.range(16, 30)
.collect(
StringBuilder::new,
(builder, i) -> builder.append(i).append(", "),
StringBuilder::append);
if (sb.length() != 0) {
sb.setLength(sb.length() - 2);
}
String result = sb.toString();
With IntStream.mapToObj:
String s = IntStream.range(16, 30)
.mapToObj(String::valueOf)
.collect(Collectors.joining(","));
If your sense of convenience implies less code, there is no need to explicitly map the Long to String. They can be simply mapped while collecting process. Here's the code to do so:
List<String> str = LongStream.range(16, 30)
.boxed()
.collect(Collectors.mapping(l -> String.valueOf(l), Collectors.toList()));
System.out.println(str.toString());
The result is:
[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
In response to the comment to whether boxing should be avoided, it has to happen anyway. The range() in this case returns an object of LongStream which has to be converted to a stream of Long.
According to #Jubobs The mapToObject() effectively returns stream of Long from the LongStream object.
For example if I have a string {a, b, c}. I need to print out on the console all the permutations without repeating letters from 1 letter to 3 letters like this:
a b c ab ac abc acb ba bc bac bca ca cb cab cba
How can I write this using recursion?
If you need all the permutations of the chars into a String you can use a recursive function.
Here's the code in Swift.
func visit(unused:[Character], used: [Character] = [Character]()) -> [String] {
var result = [String(used)]
for (index, char) in unused.enumerate() {
var unused = unused
unused.removeAtIndex(index)
var used = used
used.append(char)
result = result + visit(unused, used: used)
}
return result
}
As you can see the function receives 2 params:
unused: represents the list of chars not yet used
used: the chars used to build possible element of the ouput. This parameter is optional so if it's not passed to the function, an empty array is used (this is useful for the first invocation).
Test
let word = "abc"
let chars = [Character](word.characters)
print(visit(chars))
["", "a", "ab", "abc", "ac", "acb", "b", "ba", "bac", "bc", "bca", "c", "ca", "cab", "cb", "cba"]
Omitting the empty String
This results also contains the empty String but you can easily omit this value just update the function as shown below.
func visit(unused:[Character], used: [Character] = [Character]()) -> [String] {
var result = [String]()
if !used.isEmpty {
result.append(String(used))
}
for (index, char) in unused.enumerate() {
var unused = unused
unused.removeAtIndex(index)
var used = used
used.append(char)
result = result + visit(unused, used: used)
}
return result
}
I have a column type int and I want to check the value to display string instead of the number.
I added this expression
=IIf(Fields!Categ.Value = 1, "First", "")
=IIf(Fields!Categ.Value = 2, "Second", "")
=IIf(Fields!Categ.Value = 3, "Third", "")
=IIf(Fields!Categ.Value = 4, "Fourth", "")
=IIf(Fields!Categ.Value = 5, "Fifth", "")
but it is not working.
How can I do it?
Can you try this?
=Switch(
Fields!Categ.Value = 1, "First",
Fields!Categ.Value = 2, "Second",
Fields!Categ.Value = 3, "Third",
Fields!Categ.Value = 4, "Fourth",
Fields!Categ.Value = 5, "Fifth",
Fields!Categ.Value > 5, "",
Fields!Categ.Value < 1, "",
)
I believe the values returned by the .value condition are strings so you could either have to cast them or do a string comparison.
String Comparison
=IIf(Fields!Categ.Value == "1", "First", "")
Int Cast
=IIf(Int.Parse(Fields!Categ.Value) == 1, "First", "")
Its been awhile since I have worked with RDLC.
To add embedded code to a report, use the Code tab of the Report Properties dialog box. The code block you create can contain multiple methods. Methods in embedded code must be written in Microsoft Visual Basic and must be instance-based.
so for example:
public function getDescription(cat as integer) as string
dim sRet as string
select case cat
case 1: sRet = "first"
case 2: sRet = "second"
case else
sRet = "uh?"
end select
return sRet
end function
then use the expression:
=code.getDescription(fields!Categ.value)