Hope you all are doing well in these times.
here's my code:
def ab(n):
first = 0
last = -1
endprod = n[first] + n[last]
endprod2 = n[first+1] + n[last-1]
endprod3 = n[first+2] + n[last-2]
endprod4 = n[first+3] + n[last-3]
endprod5 = n[first+4] + n[last-4]
endprod100 = endprod[::-1] + endprod2[::-1] + endprod3[::-1]+ endprod4[::-1]+ endprod5[::-1]
return endprod100
I was able do to it, however mine isn't a loop. Is there a way to convert my code into a for loop. So, increment by 1 and decrement by 1.
Thanks,
Try this:
def ab(n):
result = ''
for j in range(len(n) // 2):
result += n[-j-1] + n[j]
if len(n) % 2 == 1:
result += n[len(n) // 2]
return result
You also need the part
if len(n) % 2 == 1:
result += n[len(n) // 2]
because your input string might have an odd number of characters
Examples:
>>> ab('0123456789')
'9081726354'
>>> ab('01234956789')
'90817263549'
If you want to reuse your original logic:
def ab(n):
result = ''
first = 0
last = -1
for j in range(len(n) // 2):
result += n[last-j] + n[first+j]
if len(n) % 2 == 1:
result += n[len(n) // 2]
return result
You could also recurse it:
def ab(s):
if len(s)>2:
return s[-1]+s[0]+ab(s[1:-1])
else:
return s
But the last part of Riccardo's answer fits your question more closely.
you need to split your string for your loop, means first you broke your string to half then build your string, you could use zip to iterate over multiple iteratable. something like this:
def ab(s):
out = ""
for v0,v1 in zip(s[:len(s)//2 -1 :-1], s[:len(s)//2 ]):
out += v0 + v1
return out
the better version you should write without loop.
like this:
out = "".join(map(lambda x: "".join(x), zip(s[:len(s)//2 -1 :-1], s[:len(s)//2 ])))
There are already a lot of good answers that are probably clearer, easier to read and much better suited for learning purposes than what I'm about to write. BUT... something I wanted to bring up (maybe just telling myself, because I tend to forget it) is that sometimes destroying the original argument can facilitate this sort of things.
In this case, you could keep "poping" left and right, shrinking your string until you exhaust it
def swap_destr(my_str):
result = ""
while len(my_str) > 1:
result += my_str[-1] # Last char
result += my_str[0] # First char
my_str = my_str[1:-1] # Shrink it!
return result + my_str # + my_str just in case there 1 char left
print(swap_destr("0123456789"))
print(swap_destr("123456789"))
# Outputs:
# 9081726354
# 918273645
This is also a good problem to see (and play with) recursion:
def swap_recur(my_str):
if len(my_str) <= 1:
return my_str
return my_str[-1] + my_str[0] + swap_recur(my_str[1:-1])
print(swap_recur("0123456789"))
print(swap_recur("123456789"))
# Outputs:
# 9081726354
# 918273645
I would like to be able to create a dictionary that combines two inputs as the key and stores the outputs of if and elif functions as the values.
my code so far looks like:
dict = {}
r = int(input("what row: "))
c = int(input("what column: "))
a = 0.0
b = 0.0
def weightOn(r,c):
global a
global b
if r < 0:
print('Not valid')
elif r == 0 and c == 0:
return a
elif r > 0 and c == 0:
a += 200 / (2 ** r)
return weightOn(r - 1, 0)
elif r > 0 and c == r:
a += 200 / (2 ** r)
return weightOn(r - 1, 0)
weightOn(r,c)
if r > c > 0:
print(b)
else:
print(a)
I would like variables r & c to be the keys, so if I input r as 2 and c as 1 it would save the value as 100. So hopefully my dictionary could look something like: dict = {2.1 :100} and so on.
You can use a tuple as key:
d = {(r,c): weightOn(r,c)}
Depending on the use-case, hierarchical dictionaries might also be useful:
d = {r : {c: weightOn(r,c)}}
To answer the question you asked, since I don't see you using a dict anywhere in your code snippet, in order to use multiple keys as a dictionary in python, you have to change the data in some way. You can use a tuple, which is essentially creating a list, but instead of using [], you use (). This is the same as a list, but it is immutable. You can also use a list. You can also convert them into a string. Something like:
key = str(r) + str(c)
Presumably if you were to do something like:
key = str(r) + " " + str(c)
dict[key] = weightOn(r, c)
You would get dict = {"2 1" :100}, so long as your function weightOn works as you intended it to.
How would I count consecutive characters in Python to see the number of times each unique digit repeats before the next unique digit?
At first, I thought I could do something like:
word = '1000'
counter = 0
print range(len(word))
for i in range(len(word) - 1):
while word[i] == word[i + 1]:
counter += 1
print counter * "0"
else:
counter = 1
print counter * "1"
So that in this manner I could see the number of times each unique digit repeats. But this, of course, falls out of range when i reaches the last value.
In the example above, I would want Python to tell me that 1 repeats 1, and that 0 repeats 3 times. The code above fails, however, because of my while statement.
How could I do this with just built-in functions?
Consecutive counts:
You can use itertools.groupby:
s = "111000222334455555"
from itertools import groupby
groups = groupby(s)
result = [(label, sum(1 for _ in group)) for label, group in groups]
After which, result looks like:
[("1": 3), ("0", 3), ("2", 3), ("3", 2), ("4", 2), ("5", 5)]
And you could format with something like:
", ".join("{}x{}".format(label, count) for label, count in result)
# "1x3, 0x3, 2x3, 3x2, 4x2, 5x5"
Total counts:
Someone in the comments is concerned that you want a total count of numbers so "11100111" -> {"1":6, "0":2}. In that case you want to use a collections.Counter:
from collections import Counter
s = "11100111"
result = Counter(s)
# {"1":6, "0":2}
Your method:
As many have pointed out, your method fails because you're looping through range(len(s)) but addressing s[i+1]. This leads to an off-by-one error when i is pointing at the last index of s, so i+1 raises an IndexError. One way to fix this would be to loop through range(len(s)-1), but it's more pythonic to generate something to iterate over.
For string that's not absolutely huge, zip(s, s[1:]) isn't a a performance issue, so you could do:
counts = []
count = 1
for a, b in zip(s, s[1:]):
if a==b:
count += 1
else:
counts.append((a, count))
count = 1
The only problem being that you'll have to special-case the last character if it's unique. That can be fixed with itertools.zip_longest
import itertools
counts = []
count = 1
for a, b in itertools.zip_longest(s, s[1:], fillvalue=None):
if a==b:
count += 1
else:
counts.append((a, count))
count = 1
If you do have a truly huge string and can't stand to hold two of them in memory at a time, you can use the itertools recipe pairwise.
def pairwise(iterable):
"""iterates pairwise without holding an extra copy of iterable in memory"""
a, b = itertools.tee(iterable)
next(b, None)
return itertools.zip_longest(a, b, fillvalue=None)
counts = []
count = 1
for a, b in pairwise(s):
...
A solution "that way", with only basic statements:
word="100011010" #word = "1"
count=1
length=""
if len(word)>1:
for i in range(1,len(word)):
if word[i-1]==word[i]:
count+=1
else :
length += word[i-1]+" repeats "+str(count)+", "
count=1
length += ("and "+word[i]+" repeats "+str(count))
else:
i=0
length += ("and "+word[i]+" repeats "+str(count))
print (length)
Output :
'1 repeats 1, 0 repeats 3, 1 repeats 2, 0 repeats 1, 1 repeats 1, and 0 repeats 1'
#'1 repeats 1'
Totals (without sub-groupings)
#!/usr/bin/python3 -B
charseq = 'abbcccdddd'
distros = { c:1 for c in charseq }
for c in range(len(charseq)-1):
if charseq[c] == charseq[c+1]:
distros[charseq[c]] += 1
print(distros)
I'll provide a brief explanation for the interesting lines.
distros = { c:1 for c in charseq }
The line above is a dictionary comprehension, and it basically iterates over the characters in charseq and creates a key/value pair for a dictionary where the key is the character and the value is the number of times it has been encountered so far.
Then comes the loop:
for c in range(len(charseq)-1):
We go from 0 to length - 1 to avoid going out of bounds with the c+1 indexing in the loop's body.
if charseq[c] == charseq[c+1]:
distros[charseq[c]] += 1
At this point, every match we encounter we know is consecutive, so we simply add 1 to the character key. For example, if we take a snapshot of one iteration, the code could look like this (using direct values instead of variables, for illustrative purposes):
# replacing vars for their values
if charseq[1] == charseq[1+1]:
distros[charseq[1]] += 1
# this is a snapshot of a single comparison here and what happens later
if 'b' == 'b':
distros['b'] += 1
You can see the program output below with the correct counts:
➜ /tmp ./counter.py
{'b': 2, 'a': 1, 'c': 3, 'd': 4}
You only need to change len(word) to len(word) - 1. That said, you could also use the fact that False's value is 0 and True's value is 1 with sum:
sum(word[i] == word[i+1] for i in range(len(word)-1))
This produces the sum of (False, True, True, False) where False is 0 and True is 1 - which is what you're after.
If you want this to be safe you need to guard empty words (index -1 access):
sum(word[i] == word[i+1] for i in range(max(0, len(word)-1)))
And this can be improved with zip:
sum(c1 == c2 for c1, c2 in zip(word[:-1], word[1:]))
If we want to count consecutive characters without looping, we can make use of pandas:
In [1]: import pandas as pd
In [2]: sample = 'abbcccddddaaaaffaaa'
In [3]: d = pd.Series(list(sample))
In [4]: [(cat[1], grp.shape[0]) for cat, grp in d.groupby([d.ne(d.shift()).cumsum(), d])]
Out[4]: [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 4), ('f', 2), ('a', 3)]
The key is to find the first elements that are different from their previous values and then make proper groupings in pandas:
In [5]: sample = 'abba'
In [6]: d = pd.Series(list(sample))
In [7]: d.ne(d.shift())
Out[7]:
0 True
1 True
2 False
3 True
dtype: bool
In [8]: d.ne(d.shift()).cumsum()
Out[8]:
0 1
1 2
2 2
3 3
dtype: int32
This is my simple code for finding maximum number of consecutive 1's in binaray string in python 3:
count= 0
maxcount = 0
for i in str(bin(13)):
if i == '1':
count +=1
elif count > maxcount:
maxcount = count;
count = 0
else:
count = 0
if count > maxcount: maxcount = count
maxcount
There is no need to count or groupby. Just note the indices where a change occurs and subtract consecutive indicies.
w = "111000222334455555"
iw = [0] + [i+1 for i in range(len(w)-1) if w[i] != w[i+1]] + [len(w)]
dw = [w[i] for i in range(len(w)-1) if w[i] != w[i+1]] + [w[-1]]
cw = [ iw[j] - iw[j-1] for j in range(1, len(iw) ) ]
print(dw) # digits
['1', '0', '2', '3', '4']
print(cw) # counts
[3, 3, 3, 2, 2, 5]
w = 'XXYXYYYXYXXzzzzzYYY'
iw = [0] + [i+1 for i in range(len(w)-1) if w[i] != w[i+1]] + [len(w)]
dw = [w[i] for i in range(len(w)-1) if w[i] != w[i+1]] + [w[-1]]
cw = [ iw[j] - iw[j-1] for j in range(1, len(iw) ) ]
print(dw) # characters
print(cw) # digits
['X', 'Y', 'X', 'Y', 'X', 'Y', 'X', 'z', 'Y']
[2, 1, 1, 3, 1, 1, 2, 5, 3]
A one liner that returns the amount of consecutive characters with no imports:
def f(x):s=x+" ";t=[x[1] for x in zip(s[0:],s[1:],s[2:]) if (x[1]==x[0])or(x[1]==x[2])];return {h: t.count(h) for h in set(t)}
That returns the amount of times any repeated character in a list is in a consecutive run of characters.
alternatively, this accomplishes the same thing, albeit much slower:
def A(m):t=[thing for x,thing in enumerate(m) if thing in [(m[x+1] if x+1<len(m) else None),(m[x-1] if x-1>0 else None)]];return {h: t.count(h) for h in set(t)}
In terms of performance, I ran them with
site = 'https://web.njit.edu/~cm395/theBeeMovieScript/'
s = urllib.request.urlopen(site).read(100_000)
s = str(copy.deepcopy(s))
print(timeit.timeit('A(s)',globals=locals(),number=100))
print(timeit.timeit('f(s)',globals=locals(),number=100))
which resulted in:
12.528256356999918
5.351301653001428
This method can definitely be improved, but without using any external libraries, this was the best I could come up with.
In python
your_string = "wwwwweaaaawwbbbbn"
current = ''
count = 0
for index, loop in enumerate(your_string):
current = loop
count = count + 1
if index == len(your_string)-1:
print(f"{count}{current}", end ='')
break
if your_string[index+1] != current:
print(f"{count}{current}",end ='')
count = 0
continue
This will output
5w1e4a2w4b1n
#I wrote the code using simple loops and if statement
s='feeekksssh' #len(s) =11
count=1 #f:0, e:3, j:2, s:3 h:1
l=[]
for i in range(1,len(s)): #range(1,10)
if s[i-1]==s[i]:
count = count+1
else:
l.append(count)
count=1
if i == len(s)-1: #To check the last character sequence we need loop reverse order
reverse_count=1
for i in range(-1,-(len(s)),-1): #Lopping only for last character
if s[i] == s[i-1]:
reverse_count = reverse_count+1
else:
l.append(reverse_count)
break
print(l)
Today I had an interview and was asked the same question. I was struggling with the original solution in mind:
s = 'abbcccda'
old = ''
cnt = 0
res = ''
for c in s:
cnt += 1
if old != c:
res += f'{old}{cnt}'
old = c
cnt = 0 # default 0 or 1 neither work
print(res)
# 1a1b2c3d1
Sadly this solution always got unexpected edge cases result(is there anyone to fix the code? maybe i need post another question), and finally timeout the interview.
After the interview I calmed down and soon got a stable solution I think(though I like the groupby best).
s = 'abbcccda'
olds = []
for c in s:
if olds and c in olds[-1]:
olds[-1].append(c)
else:
olds.append([c])
print(olds)
res = ''.join([f'{lst[0]}{len(lst)}' for lst in olds])
print(res)
# [['a'], ['b', 'b'], ['c', 'c', 'c'], ['d'], ['a']]
# a1b2c3d1a1
Here is my simple solution:
def count_chars(s):
size = len(s)
count = 1
op = ''
for i in range(1, size):
if s[i] == s[i-1]:
count += 1
else:
op += "{}{}".format(count, s[i-1])
count = 1
if size:
op += "{}{}".format(count, s[size-1])
return op
data_input = 'aabaaaabbaaaaax'
start = 0
end = 0
temp_dict = dict()
while start < len(data_input):
if data_input[start] == data_input[end]:
end = end + 1
if end == len(data_input):
value = data_input[start:end]
temp_dict[value] = len(value)
break
if data_input[start] != data_input[end]:
value = data_input[start:end]
temp_dict[value] = len(value)
start = end
print(temp_dict)
PROBLEM: we need to count consecutive characters and return characters with their count.
def countWithString(input_string:str)-> str:
count = 1
output = ''
for i in range(1,len(input_string)):
if input_string[i]==input_string[i-1]:
count +=1
else:
output += f"{count}{input_string[i-1]}"
count = 1
# Used to add last string count (at last else condition will not run and data will not be inserted to ouput string)
output += f"{count}{input_string[-1]}"
return output
countWithString(input)
input:'aaabbbaabbcc'
output:'3a3b2a2b2c'
Time Complexity: O(n)
Space Complexity: O(1)
temp_str = "aaaajjbbbeeeeewwjjj"
def consecutive_charcounter(input_str):
counter = 0
temp_list = []
for i in range(len(input_str)):
if i==0:
counter+=1
elif input_str[i]== input_str[i-1]:
counter+=1
if i == len(input_str)-1:
temp_list.extend([input_str[i - 1], str(counter)])
else:
temp_list.extend([input_str[i-1],str(counter)])
counter = 1
print("".join(temp_list))
consecutive_charcounter(temp_str)
My problem is to find the consecutive '3's in a list. For example list('133233313333') . What makes it difficult is only two adjacent '3's is valid, three or more adjacent '3's are not. So '33' is valid, but triple '3's and '3333' are not valid. I tried the following at first:
try:
if l[i] == '3' and l[i+1] == '3' and l[i+2] != '3' and l[i-1] != '3':
record_current(i)
except IndexError:
pass
My intention is to ignore the comparison and let it be true if there is an IndexError, but it doesn't work.
If list has a method like dict.get(), which returns None is there's an KeyError, I could write it as (l[i+2] == None or l[i+2] != '3').
If I must finish it now, I would treat the first item and the last two items sperately from the other items. But is there some way to solve this problem elegantly?
You can do this using itertools.groupby:
>>> from operator import itemgetter
>>> from itertools import groupby
>>> s = list('1332333133334433')
>>> for k, g in groupby(enumerate(s), itemgetter(1)):
if k == '3':
ind = next(g)[0]
if sum(1 for _ in g) == 1:
print ind
...
1
14
Count the consecutive 3s !
Keep a counter which is incremented every time you meet a '3' and reset on a non-'3'; compare to 2 before a reset:
j= 0
for i in range(len(L)):
if L[i] == '3':
j+= 1
else:
if j == 2:
print "Found at", i - j
j= 0
if j == 2:
print "Found at", i - j + 1 # Late fix (+ 1)
Alternatively, one may find successive runs of '3's and non-'3's. This way, one avoids testing j == 2 on every non-'3' element, at the expense of one extra loop test for every sequence of 3's:
i= 0
while i < len(L):
# Find the next '3'
while i < len(L) and L[i] != '3':
i+= 1
j= i
# Find the next non-'3'
while i < len(L) and L[i] == '3':
i+= 1
if i - j == 2:
print "Found at", j
You are trying to check for a certain Grammar. For this, you can implement a Deterministic Finite Automaton (or DFA).
Here is a solution that uses regular expressions:
import re
m = re.finditer('(?<!3)3{2}(?!3)', '1332333133334433')
for x in m:
print x.span()[0]
The regular expression finds all matches for two successive threes, as long as they are not followed by or preceeded by a 3. The output is:
1
14
You can substitute any character for the '3' in the regular expression, to search for that letter instead.
data = "1332333133334433"
from itertools import groupby
from operator import itemgetter
result = []
for char, grp in groupby(enumerate(data), itemgetter(1)):
groups = list(grp)
if char == "3" and len(groups) == 2:
result.append(groups[0][0])
print result
Output
[1, 14]
This returns True if '333' in the list
>>> l = "1332333133334433"
>>> any([(i[:3]=='333' and i[3] != '3') for i in map("".join,zip(l[:],l[1:],l[2:],l[3:]))])
True
you can see that:
>>> map("".join,zip(l[:],l[1:],l[2:],l[3:]))
['1332', '3323', '3233', '2333', '3331', '3313', '3133', '1333', '3333', '3334', '3344', '3443', '4433']
Here's a general solution for finding two consecutive letters that are the same:
def find_two_consecutive(my_str):
prev_letter = None
count = 1
for index, current_letter in enumerate(my_str):
if current_letter == prev_letter:
count += 1
else:
if count == 2:
print("Starting at index: %d" % (index - 2))
count = 1
prev_letter = current_letter
if count == 2:
print("Starting at index: %d" % (index - 2))
If your list really only contains one-letter elements you should use the re module:
import re
chars = list('133233313333433')
numberstr = ''.join(chars)
for match in re.finditer('(?<!3)33(?!3)', numberstr):
print(match.start())
Result:
1
13
The pattern (?<!3)33(?!3) means: find two consecutive 3s that are neither preceded nor followed by a 3.
The documentation can be found here.
Oh, and this:
chars = list('133233313333433')
numberstr = ''.join(chars)
should probably be just:
numberstr = '133233313333433'
Hi I was wondering if it was possible to assign a value in the while comparison section of the code.
Here is an example of the code currently
startIndex = find(target, key, startIndex)
while( startIndex != -1):
matchesFound += 1
startIndex = find(target, key, startIndex + 1)
return matchesFound
What I want to do is move the startIndex = find(target, key, startIndex) into the while comparison expresion so it would look something like this
while( (startIndex = find(target, key, startIndex)) != -1):
matchesFound += 1
startIndex + 1
return matchesFound
if not, what would a better refactor be?
Thanks
edit
I'm working through the MIT Open courseware 6.00 before I try out the famous 6.001 module
If for some reason you can't use a more appropriate method from whatever you're searching, this is equivalent to your working code from the question:
start = 0
count = 0
for match in iter(lambda: find(target, key, start), -1):
count += 1
start = match + 1
return count
However, you'll get the most mileage out of writing an iterator for finding these matches, "transforming" your current find function:
def findall(target, key, start=0):
for match in iter(lambda: find(target, key, start), -1):
yield match
start = match + 1
Then count from that:
count = sum(1 for m in findall(target, key))
You're writing C in Python.
Try:
startIndex = -1
while True:
startIndex = find(target, key, startIndex + 1)
if startIndex < 0:
break
matchesFound += 1
return matchesFound
Or perhaps even:
return target.count(key)
With the PEP 572 being accepted, it will be possible to do this:
while (startIndex := find(target, key, startIndex)) != -1:
matchesFound += 1
startIndex + 1
return matchesFound
It will be included in Python 3.8.
This change will make python code even smaller. Consider the following standart library code:
while True:
line = fp.readline()
if not line:
break
m = define_rx.match(line)
if m:
n, v = m.group(1, 2)
try:
v = int(v)
except ValueError:
pass
vars[n] = v
else:
m = undef_rx.match(line)
if m:
vars[m.group(1)] = 0
It will be improved like this:
while line := fp.readline():
if m := define_rx.match(line):
n, v = m.group(1, 2)
try:
v = int(v)
except ValueError:
pass
vars[n] = v
elif m := undef_rx.match(line):
vars[m.group(1)] = 0
No, you can't do that in Python. I think the main reason Python disallows this, is to avoid the frequent errors resulting from confusing assignment and equality checking.
Python claims to have readable code as a main guideline, so I think your original code is fine. No need to refactor...
i would do it like this
startIndex=0
while 1:
startIndex = find(target, key, startIndex+1)
if startIndex == -1: break
matchesFound += 1
you can put in more conditions inside the while loop like that.
Edit: #OP, in future, to count match of string, just use count
>>> mystring = "abc defabc fgh ijkabc blah"
>>> mystring.count("abc")
3
>>>
Edit.
We refactor it like this.
matches = [ k for k in range(len(target)-len(key)) if target[k:].startswith(key) ]
matchesFound = len(matches)
We don't need C-style condition and assignment conflation.
Rarely do you simply want the count; the actual locations are available for free.