Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I want to extract c=; t=; s= values to columns, in delimited string with commas in a Google sheet. The values could be repeated multiple times up to 10.
c= are always only two capital letters.
For example:
A examples
&t=clothes&t=bags&s=other&c=US&c=FR&c=GB
&c=NL
&t=glasses
&c=US&c=FR&c=GB&t=watches&t=necklaces&s=other&s=required
B column (c= strings)
US, FR, GB
C column (t= strings)
clothes,bags
D column (s= strings)
other, required
try,
=join(",", split(mid(A1, find("&c=", A1)+3, len(A1)), "&c="))
google-spreadsheet
=REGEXEXTRACT(SUBSTITUTE(A1,"&c=",","),"((?:[A-Z]{2},?)+)")
=REGEXREPLACE(REGEXREPLACE(A1,"([^A-Z]*)([A-Z]{2})([^A-Z]*)","$2, "),".$",)
[A-Z] Any 2 letter uppercase word
[^A-Z] Not a 2 letter uppercase word
Depending on your input, you may decide to follow the logic: Take all letters in Upper Case and split them by a space. The letter in UpperCase in the standard US/UK alphabet are 26 and are between 65 and 90 in the ASCII table.
Thus, looping from each value and checking whether it is between 65 and 90 would be ok:
Public Function GetUpperCase(inputVal As String) As String
Dim resultVal As String
Dim i As Long
For i = 1 To Len(inputVal)
If Asc(Mid(inputVal, i, 1)) >= 65 And Asc(Mid(inputVal, i, 1)) <= 90 Then
resultVal = resultVal & Mid(inputVal, i, 1)
Else
resultVal = resultVal & " "
End If
Next i
GetUpperCase = WorksheetFunction.Trim(resultVal)
End Function
At the end, WorksheetFunction.Trim is quite handy, because it removes the multiple spaces and it reduces them to a single one:
I prefer Jeeped's solution, but in case you were looking for a VBA implementation.
Private Sub parse_c()
Dim result As String
Dim lr As Long
lr = Cells(Rows.Count, 1).End(xlUp).Row
result = ""
For Each cell In Range("A1:A" & lr)
For i = 1 To Len(cell)
If (Mid(cell, i, 2) = "c=") Then
If (result = "") Then
result = Mid(cell, i + 2, 2)
Else
result = result & ", " & Mid(cell, i + 2, 2)
End If
End If
Next i
cell.Offset(0, 1) = result
result = ""
Next cell
End Sub
Loops through all active cells in Column A and substracts all the countries with the desired delimiter.
tested: (also with some trickery, such as =cc=UK)
Related
This question already has answers here:
Calculate string value in javascript, not using eval
(12 answers)
Closed 4 months ago.
When the text was '2+3+5+1', the logic was easy
Split('+') so the string is converted to an array.
loop over the array and calculate the sum.
check the code below
void main() {
const text = '2+3+5+1';
final array = text.split('+');
int res =0;
for (var i=0; i<= array.length -1; i++){
res+=int.parse(array[i]);;
}
print(array);
print(res);
}
Now this String "2+3-5+1" contains minus.
how to get the right response using split method?
I am using dart.
note: I don't want to use any library (math expression) to solve this exercice.
Use the .replace() method.
text = text.replace("-", "+-");
When you run through the loop, it will calculate (-).
You can split your string using regex text.split(/\+|\-/).
This of course will fail if any space is added to the string (not to mention *, / or even decimal values).
const text = '20+3-5+10';
const arr = text.split(/\+|\-/)
let tot = 0
for (const num of arr) {
const pos = text.indexOf(num)
if (pos === 0) {
tot = parseInt(num)
} else {
switch (text.substr(text.indexOf(num) - 1, 1)) {
case '+':
tot += parseInt(num)
break
case '-':
tot -= parseInt(num)
break
}
}
}
console.log(tot)
I see 2 maybe 3 options, definitely there are hundreds
You don't use split and you just iterate through the string and just add or subtract on the way. As an example
You have '2+3-5+1'. You iterate until the second operator (+ or -) on your case. When you find it you just do the operation that you have iterated through and then you just keep going. You can do it recursive or not, doesn't matter
"2+3-5+1" -> "5-5+1" -> "0+1" -> 1
You use split on + for instance and you get [ '2', '3-5', '1' ] then you go through them with a loop with 2 conditions like
if(isNaN(x)) res+= x since you know it's been divided with a +
if(!isNaN(x)) res+= x.split('-')[0] - x.split('-')[1]
isNaN -> is not a number
Ofc you can make it look nicer. If you have parenthesis though, none of this will work
You can also use regex like split(/[-+]/) or more complex, but you'll have to find a way to know what operation follows each digit. One easy approach would be to iterate through both arrays. One of numbers and one of operators
"2+3-5+1".split(/[-+]/) -> [ '2', '3', '5', '1' ]
"2+3-5+1".split(/[0-9]*/).filter(x => x) -> [ '+', '-', '+' ]
You could probably find better regex, but you get the idea
You can ofc use a map or a switch for multiple operators
My task is to generate a random string with following parameters:
At least one Uppercase
At least one lower
At least one digit
No repeated chars/digits allowed ( e.g. aa not allowed, aba is allowed, Aa is allowed)
I'm able to generate a random string with 1,2,3 parameters but parameter 4 logic is missing.
inputChars = [('a'..'z'), ('A'..'Z'),(0..9)].map(&:to_a).flatten
string = (0...16).map { inputChars[rand(inputChars.length)] }.join
require 'set'
inputChars = [('a'..'z'), ('A'..'Z'),(0..9)].map(&:to_a).flatten
set_string = Set.new
loop do
break if set_string.size == 16
cr = inputChars[rand(inputChars.length)]
set_string << cr
end
output = set_string.to_a.join
i just change your map operation to loop operation and add Set data structure to store the character from random inputChars operation. Using Set will not allow same character
Let's begin by defining two constants.
CHARS_BY_TYPE = {
lower: ('a'..'z').to_a.freeze,
upper: ('A'..'Z').to_a.freeze,
digit: ('0'..'9').to_a.freeze
}.freeze
ALL = (CHARS_BY_TYPE[:lower] + CHARS_BY_TYPE[:upper] + CHARS_BY_TYPE[:digit]).freeze
#=> [["a", "b",..., "z", "A", "B",..., "Z", "0", "1",..., "9"]
I will initially build a string of a specified length by randomly selecting one character at a time from the array ALL, ensuring that no two consecutive characters are the same. There is no assurance, however, that the resulting string will contain at least one uppercase letter, one lowercase letter and one digit.
def append_random_char(last_char)
loop do
ch = ALL.sample
break ch unless ch == last_char
end
end
Our main method will begin as follows:
def random_string(str_len)
raise ArgumentError if str_len < 3
(str_len - 2).times.with_object('') { |_,s| s << append_random_char(s[-1]) }
# ...
end
For example:
s = random_string(40)
#=> "arN64kDw6ClzcNMj8WAkj1NJC2B5oFoRlcXl5S"
str_len is the required string length, 40 in the example. Observe that s contains 38 characters of which no two successive characters are equal. We will need to add 2 characters later. If the string contained no digits, for example, at least one of those two characters added (at a random location) will be a (randomly-selected) digit. If the string were shorter and contained, for example, digits only, the two characters added will be an uppercase letter and a lowercase letter.
Next we need to see if the string is lacking an uppercase letter, a lowercase letter and/or a digit. (It cannot be missing all three, as the string must contain at least three characters.)
require 'set'
def types_to_add(str)
[:lower, :upper, :digit].select do |type|
st = CHARS_BY_TYPE[type].to_set
str.each_char.none? { |ch| st.include?(ch) }
end
end
For the random string generated above we obtain:
types_to_add(s)
#=> []
meaning that the string contains at least one uppercase letter, one lowercase letter and one digit. Try this:
types_to_add(s.gsub(/\d|[A-Z]/, '')
#=> [:upper, :digit]
See Enumerable#none?. CHARS_BY_TYPE[type] is converted to a set merely to speed look-ups.
Suppose now we need to insert an uppercase letter, lowercase letter or digit to satisfy the requirement that there is at least one of each in the string. Specifically, we wish to insert a randomly-drawn character (from CHARS_BY_TYPE[:lower], CHARS_BY_TYPE[:upper] or CHARS_BY_TYPE[:digit]) at a random location in the string we are constructing, with the restriction that neither the preceding nor following character is the same character.
def insert_in_string(str, ch)
i = loop do
i = rand(str.size + 1)
next if ch == str[i]
break i if i.zero? || ch != str[i-1]
end
str.insert(i, ch)
end
For example, if we were to insert the character '0' into (a copy of) our string s (which is not needed):
insert_in_string(s.dup, '0')
#=> "arN64kDw6ClzcN0Mj8WAkj1NJC2B5oFoRlcXl5S"
s #=> "arN64kDw6ClzcNMj8WAkj1NJC2B5oFoRlcXl5S"
^
This inserts the character ch before the character in str at index i. If rand(str.size + 1) returns str.size ch is inserted after the last character of str.
Following this operation the final step is to use the method append_random_char to build the string out to the desired length.
The completed main method is as follows.
def random_string(str_len)
raise ArgumentError if str_len < 3
s = (str_len - 2).times.with_object('') { |_,s| s << append_random_char(s[-1]) }
types_to_add(s).each { |type| insert_in_string(s, CHARS_BY_TYPE[type].sample) }
(str_len - s.size).times { s << append_random_char(s[-1]) }
s
end
s = random_string(40)
#=> "PtQrVFZWUYFwiwRy3ySfAy42G1NT98J6cMVMaWeT"
s.match?(/[a-z]/)
#=> true
s.match?(/[A-Z]/)
#=> true
s.match?(/\d/)
#=> true
s.size
#=> 40
This is how I would do it (warning: Not tested. Just want to present the idea
for my algorithm). I first take a random number for the length of the resulting random string (the length will be between 4 and 16 characters). Then I determine
randomly, how many of them are upper case / lower case / digits, and based on
these decision, I generate the string, ensuring that I don't get any duplicates
in succession.
uchars=('A'..'Z').to_a
lchars=('a'..'z').to_a
dchars=('0'..'9').to_a
charmap = { u: uchars, l: lchars, d: dchars }
total_length=rand(13)+4 # Total length of string to be generated
total_u=rand(total_length-3)+1 # Total number of uchars to be generated
total_l=rand(total_length-total_u-2)+1 # Total number of lchars
total_d=total_length-total_u-total_l # Total number of digits
# Array of types to generate
chartypes=([:u]*total_u + [:l]*total_l + [:d]*total_d).shuffle
# chartypes is an array similar to [:u,:d,:d,:l,:u], where the
# symbols designate the kind of character to be generated.
# outstr : random string to be generated
outstr = charmap[chartypes.first].sample
last_char = outstr.dup
total_length.times do |index|
loop do
nextchar = charmap[chartypes[index]].sample
if nextchar != last_char
outstr << nextchar
last_char = nextchar
break
end
end
end
With Ruby, how do I replace a range of characters in a string? For instance, given teh string
hellothere
If I want to replace characters at index positions two through five inclusive with "#" to result in a string
he####here
How would I do this?
You could get a string range and replace it by setting the new character multiplied for the last index plus 1 less the first index:
def replace_in_string(str, replace, start, finish)
str[start..finish] = replace * (finish + 1 - start)
str
end
p replace_in_string 'hellothere', '#', 2, 5
# "he####here"
I want to create list of all occurences of "x" string in range. This is my sheet:
And I want to search all occurences and list them and give proper names:
For example for G2, I want "Beret Grey" string as result. I think that I need to use array formula or something like that.
Let me first preface this that vba would be much more robust, but this formula will get you there. It may be slow as it is an array type formula and is doing a lot of calculations. These calculations only expound exponentially as the number of cells with them in it increases:
=IFERROR(INDEX(A:A,AGGREGATE(15,6,ROW($B$2:$G$7)/($B$2:$G$7="x"),ROW(1:1))) & " " & INDEX($1:$1,AGGREGATE(15,6,COLUMN(INDEX(A:G,AGGREGATE(15,6,ROW($B$2:$G$7)/($B$2:$G$7="x"),ROW(1:1)),0))/(INDEX(A:G,AGGREGATE(15,6,ROW($B$2:$G$7)/($B$2:$G$7="x"),ROW(1:1)),0)="x"),ROW(1:1)-COUNTIF($B$1:INDEX(G:G,AGGREGATE(15,6,ROW($B$2:$G$7)/($B$2:$G$7="x"),ROW(1:1)) -1),"x"))),"")
You will need to expand the range to what you need. Change all the $B$2:$G$7 to $B$2:$N$29. Do not use full column references outside those that I have used. It will kill Excel.
Also note what is and what is not relative references, they need to remain the same or you will get errors as the formula is dragged/copied down.
As simple UDF to do what you want:
Function findMatch(rng As Range, crit As String, inst As Long) As String
Dim rngArr() As Variant
rngArr = rng.Value
Dim i&, j&, k&
k = 0
If k > Application.WorksheetFunction.CountIf(rng, crit) Then
findMatch = ""
Exit Function
End If
For i = LBound(rngArr, 1) + 1 To UBound(rngArr, 1)
For j = LBound(rngArr, 2) + 1 To UBound(rngArr, 2)
If rngArr(i, j) = crit Then
k = k + 1
If k = inst Then
findMatch = rngArr(i, 1) & " " & rngArr(1, j)
Exit Function
End If
End If
Next j
Next i
then you would call it like this:
=findMatch($A$1:$G$7,"x",ROW(1:1))
And drag/copy down.
I need to parse numeric range from User Form (VBA, Excel).
For example: {1-3,5,9} -> {1,2,3,5,9,}
The question has already been discussed here:
Advanced parsing of numeric ranges from string
The task is quite standart, is there any ready-made solution for VBA?
I am assuming that the encompassing braces are not part of the string. Consider the following UDF:
Public Function DashFiller(sIn As String) As String
Dim N As Long, NN As Long
DashFiller = ""
ary = Split(sIn, ",")
For N = LBound(ary) To UBound(ary)
If InStr(1, ary(N), "-") > 0 Then
ary2 = Split(ary(N), "-")
ary(N) = ary2(0)
For NN = ary2(0) + 1 To ary2(1)
ary(N) = ary(N) & "," & NN
Next NN
End If
Next N
DashFiller = Join(ary, ",")
End Function
So if A1 contains:
1-2,3,4,5,9-12
and B1 contains:
=DashFiller(A1)
then B1 would display:
1,2,3,4,5,9,10,11,12