this question feels like it would have been asked already, but I've not found anything so here goes...
I have constructor which is handed a string which is delimited. From that string I need to populate an object's instance variables. I can easily split the string by the delimited to give me an array of strings. I know I can simply iterate through the array and set my instance variables using ifs or a switch/case statement based on the current array index - however that just feels a bit nasty. Pseudo code:
String[] tokens = <from generic string tokenizer>;
for (int i = 0;i < tokens.length;i++) {
switch(i) {
case(0): instanceVariableA = tokens[i];
case(1): instanceVarliableB = tokens[i];
...
}
}
Does anyone have any ideas of how I do this better/nicer?
For what it's worth, I'm working in Java, but I guess this is language independant.
Uhm... "nasty" is in the way the constructor handles the parameters. If you can't change that then your code snippet is as good as it may be.
You could get rid of the for loop, though...
instanceVariableA = tokens[0];
instanceVariableB = tokens[1];
and then introduce constants (for readibilty):
instanceVariableA = tokens[VARIABLE_A_INDEX];
instanceVariableB = tokens[VARIABLE_B_INDEX];
NOTE: if you could change the string parameter syntax you could introduce a simple parser and, with a little bit of reflection, handle this thing in a slightly more elegant way:
String inputString = "instanceVariableA=some_stuff|instanceVariableB=some other stuff";
String[] tokens = inputString.split("|");
for (String token : tokens)
{
String[] elements = token.split("=");
String propertyName = tokens[0];
String propertyValue = tokens[1];
invokeSetter(this, propertyName, propertyValue); // TODO write method
}
Could you not use a "for-each" loop to eliminate much of the clutter?
I really think the way you are doing it is fine, and Manrico makes a good suggestion about using constants as well.
Another method would be to create a HashMap with integer keys and string values where the key is the index and the value is the name of the property. You could then use a simple loop and some reflection to set the properties. The reflection part might make this a bit slow, but in another language (say, PHP for example) this would be much cleaner.
just an untested idea,
keep the original token...
String[] tokens = <from generic string tokenizer>;
then create
int instanceVariableA = 0;
int instanceVariableB = 1;
if you need to use it, then just
tokens[instanceVariableA];
hence no more loops, no more VARIABLE_A_INDEX...
maybe JSON might help?
Python-specific solution:
Let's say params = ["instanceVariableA", "instanceVariableB"]. Then:
self.__dict__.update(dict(zip(params, tokens)))
should work; that's roughly equivalent to
for k,v in zip(params, tokens):
setAttr(self, k, v)
depending on the presence/absence of accessors.
In a non-dynamic language, you could accomplish the same effect building a mapping from strings to references/accessors of some kind.
(Also beware that zip stops when either list runs out.)
Related
I have a sequence of code points as Sequence<Int>.
I want to get this into a String.
What I currently do is this:
val string = codePoints
.map { codePoint -> String(intArrayOf(codePoint), 0, 1) }
.joinToString()
But it feels extremely hairy to create a string for each code point just to concatenate them immediately after. Is there a more direct way to do this?
So far the best I was able to do was something like this:
val string2 = codePoints.toList().toIntArray()
.let { codePoints -> String(codePoints, 0, codePoints.size) }
The amount of code isn't really any better, and it has a toList().toIntArray() which I'm not completely fond of. But it at least avoids the packaging of everything into dozens of one-code-point strings, and the logic is still written in the logical order.
You can either go for the simple:
val string = codePoints.joinToString("") { Character.toString(it) }
// or
val string = codePoints.joinToString("", transform = Character::toString)
Or use a string builder:
fun Sequence<Int>.codePointsToString(): String = buildString {
this#codePointsToString.forEach { cp ->
appendCodePoint(cp)
}
}
This second one expresses exactly what you want, and may benefit from future optimizations in the string builder.
it feels extremely hairy to create a string for each code point just to concatenate them immediately after
Did you really measure a performance issue with the extra string objects created here? Using toList() would also create a bunch of object arrays behind the scenes (one for each resize), which is a bit less, but not tremendously better. And as you pointed out toIntArray on top of that is yet another array creation.
Unless you know the number of elements in the sequence up front, I don't believe there is much you can do about that (the string builder approach will also likely use a resizable array behind the scenes, but at least you don't need extra array copies).
val result = codePoints.map { Character.toString(it) }.joinToString("")
Edit, based on Joffrey's comment below:
val result = codePoints.joinToString("") { Character.toString(it) }
Additional edit, full example:
val codePoints: Sequence<Int> = sequenceOf(
'a'.code,
Character.toCodePoint(0xD83D.toChar(), 0xDE03.toChar()),
Character.toCodePoint(0xD83D.toChar(), 0xDE04.toChar()),
Character.toCodePoint(0xD83D.toChar(), 0xDE05.toChar())
)
val result = codePoints.joinToString("") { Character.toString(it) }
println(result)
This will print: a😃😄😅
What would be the kotlin way to handle multiple string concatenation?
--edit--
placing the piece of code that led me to this doubt
fun getNCharsFromRange(n: Int, range: CharRange): String {
val chars = range.toList()
val buffer = StringBuffer()
while (buffer.length < n) {
val randomInt = Random.Default.nextInt(0, chars.lastIndex)
val newchar = chars[randomInt]
val lastChar = buffer.lastOrNull() ?: ""
if (newchar != lastChar) {
buffer.append(newchar)
}
}
return buffer.toString()
}
A StringBuilder is the standard way to construct a String in Kotlin, as in Java.
(Unless it can be done in one line, of course, where a string template is usually better than Java-style concatenation.)
Kotlin has one improvement, though: you can use buildString to handle that implicitly, which can make the code a little more concise. For example, your code can be written as:
fun getNCharsFromRange(n: Int, range: CharRange): String {
val chars = range.toList()
return buildString {
while (length < n) {
val randomInt = Random.Default.nextInt(0, chars.lastIndex)
val newChar = chars[randomInt]
val lastChar = lastOrNull() ?: ""
if (newChar != lastChar)
append(newChar)
}
}
}
That has no mention of buffer: instead, buildString creates a StringBuilder, makes it available as this, and then returns the resulting String. (So length, lastOrNull(), and append refer to the StringBuilder.)
For short code, this can be significantly more concise and clearer; though the benefits are much less clear with longer code. (Your code may be in the grey area between…)
Worth pointing out that the function name is misleading: it avoids repeated characters, but allows duplicates that are not consecutive. If that's deliberate, then it would be worth making clear in the function name (and/or its doc comment). Alternatively, if the intent is to avoid all duplicates, then there's an approach which is much simpler and/or more efficient: to shuffle the range (or at least part of it).
Using existing library functions, and making it an extension function on CharRange, the whole thing could be as simple as:
fun CharRange.randomChars(n: Int) = shuffled().take(n).joinToString("")
That shuffles the whole list, even if only a few characters are needed.  So it would be even more efficient to shuffle just the part needed. But there's no library function for that, so you'd have to write that manually. I'll leave it as an exercise!
How can I have a string list in ActionScript-3 ? I don't want to just use an array and always check for type or be type vulnerable. If I could make arrays type specific that would help me. In C# I can do:
List<string> list = new List<string>();
list.Add("hello world");
string str = list[0];
I wished I could do more or less the same in AS-3.
You can use the Vector type, like this:
var list:Vector.<String> = new <String>[];
// or new Vector.<String>();
list.push("hello world");
var str:String = list[0];
Note that AS3's Vector class lacks some functionality found in C#'s List<T> like Remove(), and includes some functionality you would find in other classes of C# such as pop(), typically found in Stack<T>.
In AS3 you can use Vector datatype.
See More info on Vector data type
Vector class works the same way as you need.
See http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Vector.html.
Is there a standard function in D to check if a string is alphanumeric? If not what'd be the most efficient way to do it? I'm guessing there are better ways than looping through the string and checking if the character is in between a range?
I don't think there's a single pre-made function for it, but you could compose two phobos functions (which imo is just as good!):
import std.algorithm, std.ascii;
bool good = all!isAlphaNum(your_string);
I think that does unnecessary utf decoding, so it wouldn't be maximally efficient but that's likely irrelevant for this anyway since the strings are surely short. But if that matters to you perhaps using .representation (from std.string iirc) or foreach(char c; your_string) isAlphaNum(c); yourself would be a bit faster.
I think Adam D. Ruppe's solution may be a better one, but this can also be done using regular expressions. You can view an explanation of the regular expression here.
import std.regex;
import std.stdio;
void main()
{
// Compile-time regexes are preferred
// auto alnumRegex = regex(`^[A-Za-z][A-Za-z0-9]*$`);
// Backticks represent raw strings (convenient for regexes)
enum alnumRegex = ctRegex!(`^[A-Za-z][A-Za-z0-9]*$`);
auto testString = "abc123";
auto matchResult = match(testString, alnumRegex);
if(matchResult)
{
writefln("Match(es) found: %s", matchResult);
}
else
{
writeln("Match not found");
}
}
Of course, this only works for ASCII as well.
Preface: I'm working with Processing and I've never used Java.
I have this Processing function, designed to find and return the most common color among the pixels of the current image that I'm working on. the last line complains that "The method color(int) in the type PApplet is not applicable for the arguments (String)." What's up?
color getModeColor() {
HashMap colors = new HashMap();
loadPixels();
for (int i=0; i < pixels.length; i++) {
if (colors.containsKey(hex(pixels[i]))) {
colors.put(hex(pixels[i]), (Integer)colors.get(hex(pixels[i])) + 1);
} else {
colors.put(hex(pixels[i]),1);
}
}
String highColor;
int highColorCount = 0;
Iterator i = colors.entrySet().iterator();
while (i.hasNext()) {
Map.Entry me = (Map.Entry)i.next();
if ((Integer)me.getValue() > highColorCount) {
highColorCount = (Integer)me.getValue();
highColor = (String)me.getKey();
}
}
return color((highColor);
}
The Processing docs that I'm looking at are pretty sparse on the HashMap so I'm not really sure what's going on inside it, but I've been augmenting what's available there with Java docs they point to. But I'm not really grokking what's happening with the types. It looks like the key in the HashMap needs to be a string and the value needs to be an integer, but they come out as objects that I have to cast before using. So I'm not sure whether that's causing this glitch.
Or maybe there's just a problem with color() but the docs say that it'll take a hex value which is what I was trying to use as the key in the HashMap (where I'd rather just use the color itself).
Now that I've talked through this, I'm thinking that the color() function sees the hex value as an int but the hex() function converts a color to a string. And I don't seem to be able to convert that string to an int. I guess I could parse the substrings and reconstruct the color, but there must be some more elegant way to do this that I'm missing. Should I just create a key-value-pair class that'll hold a color and a count and use an arraylist of those?
Thanks in advance for any help or suggestions you can provide!
I'll dig deeper into this, but an initial thought is to employ Java generics so that the compiler will complain about type issues (and you won't get runtime errors):
HashMap<String,Integer> colors = new HashMap<String,Integer>();
So the compiler will know that keys are Strings and elements are Integers. Thus, no casting will be necessary.
I didn't figure it out, but I did work around it. I'm just making my own string from the color components like:
colors.put(red(pixels[i]) + "," + green(pixels[i]) + "," + blue(pixels[i]),1)
and then letting the function drop a color out like this:
String[] colorConstituents = split(highColor, ",");
return color(int(colorConstituents[0]), int(colorConstituents[1]), int(colorConstituents[2]));
This doesn't really seem like the best way to handle it -- if I'm messing with this long-term I guess I'll change it to use an arraylist of objects that hold the color and count, but this works for now.