I'm using strict mode. I've got the following code:
const a: string[] = [];
// something that fills `a`
while (a.length > 0) {
const i: string = a.pop(); // Error on this line
console.log(i);
// maybe add something else to `a`
}
In this case, I'm getting an error on the line, because pop has a return type of string | null. Which makes sense for a general use of the pop method, but I'm guarding against that case? Is there a more typescript way of doing this to get the proper typeguard?
I can't just loop over the array (e.g. using of), because sometimes a gets new items added to it during the loop and the semantics aren't the same.
Why don't you check pop data before you pop?
if (a[a.length -1]) {
const i: string = a.pop()
}
a.pop()
Related
I am trying to remove all the parentheses in a string. Not thinking about it too hard, I just do a simple regexp replace (i.e. the problem in question is not particularly about getting rid of arbitrary levels of nested parentheses, but feel free to suggest a better way of doing that in a comment if you want).
use regex::Regex;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let input = "Text (with some (nested) parentheses)!";
let re = Regex::new(r"\([^()]*\)")?;
let output = re.replace_all(&input, "");
let output = re.replace_all(&output, "");
// let output = re.replace_all(&output, "");
// let output = re.replace_all(&output, "");
// let output = re.replace_all(&output, "");
// let output = re.replace_all(&output, "");
// ...
assert_eq!("Text !", output);
println!("Works!");
Ok(())
}
Because I do not know how nested the parentheses will be, I need to do the replacement in a loop rather than repeating it "just enough times". Creating a loop, however, creates a new scope and that's where I'm hitting a dead point in the discussion with the borrow checker.
The simplest case that shows what I am trying to do in the loop would be:
let mut output = re.replace_all(&input, "");
while re.is_match(&output) {
output = re.replace_all(&output, "");
}
However that cannot be done because I am assigning to a borrowed variable:
error[E0506]: cannot assign to `output` because it is borrowed
--> src/main.rs:9:9
|
9 | output = re.replace_all(&output, "");
| ^^^^^^ ------- borrow of `output` occurs here
| |
| assignment to borrowed `output` occurs here
| borrow later used here
What I would like to do, ideally, is to create new variable binding with the same name, but using let output = will shadow the outer variable binding, so the loop would cycle infinitely.
No matter what inner or outer temporary variable I create I cannot make it do what I want. I also tried using the fact that re.replace_all() returns Cow and tried using .to_owned() and .to_string() in a couple of places, but that didn't help either.
Here's a link to a playground.
re.replace_all() returns Cow
This is the root of the problem. The compiler knows that the return value might reference output, but it will also replace output, causing output to be dropped right away. If it allowed this, the reference would point to unallocated memory, leading to memory unsafety.
The solution is to avoid borrowing at all.
tried using .to_owned()
to_owned on a Cow just returns the same Cow. Perhaps you meant into_owned?
let mut output = re.replace_all(&input, "").into_owned();
while re.is_match(&output) {
output = re.replace_all(&output, "").into_owned();
}
and .to_string() in a couple of places
This works as well:
let mut output = re.replace_all(&input, "").to_string();
while re.is_match(&output) {
output = re.replace_all(&output, "").to_string();
}
Shepmaster's answer works, but it's not as efficient as it could be. A subtle property of the Cow type is that by inspecting it, we can determine whether the string was modified, and skip work if it wasn't.
Due to constraints of the Rust type system, if the value was not modified then Cow::into_owned() makes a copy. (Cow::into_owned() of a modified value does not copy). (into_owned documentation)
In your use case, we can detect unmodified Cow -- Cow::Borrowed -- and skip into_owned().
let mut output = /* mutable String */;
while re.is_match(&output).unwrap() {
match re.replace_all(&output, "") {
// Unmodified -- skip copy
Cow::Borrowed(_) => {}
// replace_all() returned a new value that we already own
Cow::Owned(new) => output = new,
}
}
But we can go further. Calling both is_match() and replace_all() means the pattern is matched twice. With our new knowledge of Cows, we can optimize that away:
let mut output = /* mutable String */;
// Cow::Owned is returned when the string was modified.
while let Cow::Owned(new) = re.replace_all(&output, "") {
output = new;
}
Edit: If your input value is immutable, you can avoid the .to_string() copy by making it Cow as well:
let input = "value";
let mut output = Cow::from(input);
while let Cow::Owned(new) = re.replace_all(&output, "") {
output = Cow::Owned(new);
}
I have this line of code:
if ((self.datasource?.contains((self.textField?.text)!)) != nil) {
if let _ = self.placeHolderWhileSelecting {
// some code
}
Is there more clear way to check if the element contains in array? I have Bool returned by contains function, I dont want to check if this Bool is nil
Edit: the solution is to change array to non-optional type.
You can use the if let where construct. This code prevent crashes if self.datasource or self.textField are nil
if let
list = self.datasource,
elm = self.textField?.text,
_ = self.placeHolderWhileSelecting
where list.contains(elm) {
// your code
}
Another possible solution, using optional chaining to get to self.textField.text and assuming datasource remains optional.
if let unwrappedArray = self.datasource, let unwrappedString = self.textField?.text {
if unwrappedArray.contains(unwrappedString) {
// Some code
}
}
I found a url request having suspicious code to one of my Drupal site. Will someone explain what will be the depth of this code and advise any precautions to be taken. Code:
function (){try{var _0x5757=["/x6C/x65/x6E/x67/x74/x68","/x72/x61/x6E/x64/x6F/x6D","/x66/x6C/x6F/x6F/x72"],_0xa438x1=this[_0x5757[0]],_0xa438x2,_0xa438x3;if(_0xa438x1==0){return};while(--_0xa438x1){_0xa438x2=Math[_0x5757[2]](Math[_0x5757[1]]()*(_0xa438x1 1));_0xa438x3=this[_0xa438x1];this[_0xa438x1]=this[_0xa438x2];this[_0xa438x2]=_0xa438x3;};}catch(e){}finally{return this}}
Site returned page not found error and I observed no issues.
Run this code through a beatifier and you will receive:
function () {
try {
var _0x5757 = ["/x6C/x65/x6E/x67/x74/x68", "/x72/x61/x6E/x64/x6F/x6D", "/x66/x6C/x6F/x6F/x72"],
_0xa438x1 = this[_0x5757[0]],
_0xa438x2, _0xa438x3;
if (_0xa438x1 == 0) {
return
};
while (--_0xa438x1) {
_0xa438x2 = Math[_0x5757[2]](Math[_0x5757[1]]() * (_0xa438x1 1));
_0xa438x3 = this[_0xa438x1];
this[_0xa438x1] = this[_0xa438x2];
this[_0xa438x2] = _0xa438x3;
};
} catch (e) {} finally {
return this
}
}
First, let's rename some variables and decrypt the array of strings in the third line. I've renamed _0x5757 to arr and escaped the hex-chars within the array. That gives you:
var arr = ["length", "random", "floor"],
So here we have a list of functions that will be used shortly. Substitute the strings in and rename the variables and you will receive:
function () {
try {
var arr = ["length", "random", "floor"],
length_func = "length",
rand_number, temp;
if (length_func == 0) {
return
};
while (--length_func) {
rand_number = Math["floor"](Math["random"]() * (length_func 1));
temp = this[length_func];
this[length_func] = this[rand_number];
this[rand_number] = temp;
};
} catch (e) {} finally {
return this
}
}
Notice how there is a syntax error in the script when generating a random number.
* (length_func 1)
with length_func = "length" is not valid JavaScript syntax, so the code is actually not functional. I can still make a guess on what it was supposed to do: If we remove the obfuscation of calling a function by doing Math["floor"] instead of Math.floor() the important lines are
while (--length_func) {
rand_number = Math.floor( Math.random() * ( length 1 ));
temp = this.length_func;
this.length_func = this.rand_number;
this.rand_number = temp;
};
It seems that it tries to compute a random integer using Math.random() and Math.floor(), then swaps the contents of the variables length_func and rand_numerber, all wrapped in a while(--length_func) loop. There's nothing functional here or anything that makes sense. An attempt at an infinte loop hanging the browser maybe? The code is, as it stands, non-functional. It even fails to generate a random number, because Math.floor() will always round-down the inputted float, and Math.rand() will generate a number within 0.0 to 1.0, so nearly always something slightly below 1.0, therefore rand_number = 0 for most of the time. The multiplication with the rand() output with the length_func 1 maybe should have made the number bigger, but the syntax is invalid. When I use my browser's console to execute length, it gives me 0, when I try to do length(1), then length is not a function, the only length that makes sense here is a string-length or array length, but then it would have to explicitly be "someString".length. Hope this helps you.
I am learning how to build apps and working with Swift for this project.
I had a buddy help me pull data in from a website and it looks like he created classes with variables and mapped them to certain extensions (IE "Username") so when I call the variable data such as profile I would call it. The below uses luck_30 able to store "Stats.luck_30"
luck_30.text = profile.luck_30
So inside one of my variables that is in this "Profile" class is setup into an array. I can pull the array out of the class, but I can't seem to do for while statement replacing the [#] with a variable from the for command.
func aliveWorkers(profile: Profile) -> NSNumber{
var myworkers : Array = profile.workers!
//this test works and returns the proper value
var testworker: NSNumber = myworkers[0].alive!
println("The satus of the test worker is " + testworker.description)
/* This code is giving error "Could not find member alive" it does not ifor var
for ifor in myworkers{
var thisworker: NSNumber = myworkers[ifor].alive! as NSNumber
}
*/
return 42
}
Your variable ifor is not a counter, it is an actual object. You could do something like this:
for worker in myWorkers {
let workerIsAlive = worker.alive!
}
Alternatively, if you need the index,
for i in 0 ..< myWorkers.count {
let worker = myWorkers[i]
let workerIsAlive = worker.alive!
}
If you need both:
for (i, worker) in enumerate(myWorkers) {
let workerIsAlive = worker.alive!
}
And as a matter of style, I would stay away from NSNumber and use Int or Bool or whatever the data actually is. Also, it looks like the alive variable should not be optional, as you're unwrapping it everywhere. To avoid "mysterious" crashes later, you may want to think about making it a non-optional type.
when using a for in loop, your loop variable isn't an index, its the objects you're looping through. so..
func aliveWorkers() {
var myworkers = [1, 2, 3]
//this test works and returns the proper value
let testworker = myworkers[0]
print("The satus of the test worker is \(testworker)")
for ifor in myworkers {
print(ifor)
}
}
Notice a few things... you don't need to use + to concatenate those strings. you can just use string interpolation. \(variable) inserts the value of variable in the string.
Try to use let instead of var when you don't change the variable. You don't need to explicitly define type on variables either.
Here's what I'm currently doing/trying to do to accomplish my goal. But it is not removing the "row" the way I would like it too.
So, I'm making an object, then pushing it into an array. And the adding to the array part works fine and just as I expect.
var nearProfileInfoObj:Object = new Object();
nearProfileInfoObj.type = "userInfo";
nearProfileInfoObj.dowhat = "add";
nearProfileInfoObj.userid = netConnection.nearID;
nearProfileInfoObj.username = username_input_txt.text;
nearProfileInfoObj.sex = sex_input_txt.selectedItem.toString();
nearProfileInfoObj.age = age_input_txt.selectedItem;
nearProfileInfoObj.location = location_input_txt.text;
nearProfileInfoObj.headline = headline_input_txt.text;
theArray.push(nearProfileInfoObj);
So after that later on I need to be able to remove that object from the array, and it's not working the way I'm expecting. I want to take a variable whoLeft and capture their ID and then look in the array for that particular ID in the userid part of the object and if its there DELETE that whole "row".
I know you can do a filter with an array collection but that doesnt actually delete it. I need to delete it because I may be adding the same value again later on.
whoLeft = theiruserIDVariable;
theArray.filter(userLeaving);
public function userLeaving(element:*, index:int, arr:Array):Boolean
{
if (element.userid == whoLeft)
{
return false;
}
else
{
return true;
}
}
But this doesnt seem to be deleting the whole row like it implies. Does anyone know what i'm doing wrong?
Instead of modifying the original array, the new filtered array is returned by the filter method. So you need to assign the returned array to theArray.
Try this
theArray = theArray.filter(userLeaving);
EDIT This turned out to be slower than for loop:
An alternative to the hand coded loop could be something like this:
theArray.every(searchAndDestroy);
public function searchAndDestroy(element:*, index:int, arr:Array):Boolean
{
if (element.userid == whoLeft)
{
arr.splice(index,1);
return false;
}
return true;
}
As far as I know, every() terminates the first time the test function returns false. So the question is: for a big list, which is faster, the for loop or the loop that every() does with the overhead of the test function call.
EDIT #2 But this was faster than a for loop for a test I ran on an array of a million Points:
for each(var element:Object in theArray)
{
if (element.userid==whoLeft)
{
theArray.splice(theArray.indexOf(element),1);
break;
}
}
I think this is what you're looking for:
for(var i:uint = 0, len:uint = theArray.length; i<len; i++)
{
if(thisArray[i].id == whoLeft.id)
{
thisArray.splice(i, 1);
break;
}
}
However, do you really need it in an Array because you could always use a Dictionary which would mean accessing it by id which would be a lot simpler to remove.