Related
Given a string, find the first non-repeating character in it and return its index. If it doesn't exist, return -1. Input string already all lowercase.
Why does my code not work?
str1 = input("give me a string: ")
def unique(x):
stack = []
if x is None:
return (-1)
i = 0
while i < len(x):
stack = stack.append(x[i])
if x[i] in stack:
return(i)
else:
i += 1
unique(str1)
str1 = input("give me a string: ")
def unique(x):
for i in x:
if x.count(i) == 1:
return x.index(i)
else:
return -1
print(unique(str1))
This will work
Explanation
Instead of using the list stack use the count() function of the string. The function unique(x) will return the index of that first element whose count is 1 in the str1 string.
You need to know what your code is doing to figure out why it doesn't work, let's breakthrough it step by step.
you create a empty list stack for later use, that's fine.
if x is None is a strange way to check if a string is given, and it doesn't work because even a empty string "" is not equal to None. is is used to check if both sides are the same object, and == is a better operator to check if values of both sides are the same. Therefore, if x == "" is better, but if not x is even better to check if something is empty.
using variable i and while loop to iterate the string is fine.
append() change the list in-place and return None, so stack = stack.append(x[i]) is assigning None to stack.
in stack is going to raise TypeError as NoneType is not iterable. If we change the last line to stack.append(x[i]), it now works, as x[0] is already appended to stack, if x[0] in stack must be True and return 0 for your result.
That's what your code is doing, you just append the first character and return the first index. You need to go through the whole string to know if a character is unique.
Although Rishabh's answer is cleaner, I provide a way to doing it using lists to save seen and repeated characters, then read the string again to find the index of unique character.
x = input("give me a string: ")
def unique(x):
seen = []
repeated = []
for char in x:
if char in seen:
repeated.append(char)
else:
seen.append(char)
for idx, char in enumerate(x):
if char not in repeated:
return idx
return -1
print(unique(x))
I have a list of things I want to iterate over and return each of them only once per function call.
What I've tried:
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
# convert each string into list
result = [x.strip() for x in tl.split(",")]
index = 0
def func():
return result[index]
index += 1
It's saying code unreachable at the index += 1 part.
The output I want is zza the first time I call func(), then zzb, then zzc, etc.
Appreciate the help.
EDIT:
I've found this answer to work well and easily readable:
# list of elements seperated by a comma
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
# split each string by comma to get a list
result = [x.strip() for x in tl.split(",")]
# initialize the object
iterator_obj = iter(result)
print(next(iterator_obj))
print(next(iterator_obj))
print(next(iterator_obj))
output:
zza
zzb
zzc
In c++ there is an operator that will increment a variable with ++i incrementing before evaluation and i++ after evaluation
(i:=i+1) #same as ++i (increment, then return new value)
(i:=i+1)-1 #same as i++ (return the incremented value -1)
so the function you want is
def func():
global index
return result[(index := index+1)-1]
the := operator is new in python 3.8
so
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
# convert each string into list
result = [x.strip() for x in tl.split(",")]
index = 0
def func():
global index
return result[(index := index + 1) - 1]
print(func())
print(func())
print(func())
print(func())
prints
zza
zzb
zzc
zzd
Because return statement exit the function, any statement after that is not reachable. A quick fix to your code:
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
# convert each string into list
result = [x.strip() for x in tl.split(",")]
next_index = 0
def func():
global next_index
next_index += 1
return result[next_index-1]
BTW, your func behaves like built-in next. If you don't want to reinvent the wheel:
# convert each string into iterator
result = (x.strip() for x in tl.split(","))
# next(result) will get to the next item on the list
If you want to be able to only return one item of your string at a time per function call, you need to use an generator:
def func(items):
string_list = items.split(",")
for i in range(len(string_list)):
yield string_list[i]
tl = """
zza,zzb,zzc,zzd,zze,zzf,zzg,zzh,zzi,zzj,zzk,zzl,zzm,zzn,zzo,zzp,zzq,zzr,zzs,zzt,zzu,zzv,zzw,zzx,zzy,zzz
"""
item = func(tl)
To pull a value out, in order, use
next(item) # zza
next(item) # zzb
...
Every time you call next, a new value will be returned.
As an aside, anything after return statement will not run, which is why your index += 1 did not work. Return stops the function from running.
I'm trying to use pyresttest's benchmarking framework to generate a sequence of entries in my flask_sqlalchemy-based database. I would like to read input values from a pre-defined list as advertised by this framework's benchmarking generator type 'fixed_sequence', but it's only picking up the first element of the list.
Here is the issue that explains my problem in detail, with an example: https://github.com/svanoort/pyresttest/issues/264
Any pointer in the right direction will be greatly appreciated
I looked into the code, it is jsut a bug, this feature was never used by anyone.
https://github.com/svanoort/pyresttest/blob/master/pyresttest/generators.py#L100
instead of:
```
def factory_fixed_sequence(values):
""" Return a generator that runs through a list of values in order, looping after end """
def seq_generator():
my_list = list(values)
i = 0
while(True):
yield my_list[i]
if i == len(my_list):
i = 0
return seq_generator
It should be:
def factory_fixed_sequence(values):
""" Return a generator that runs through a list of values in order, looping after end """
def seq_generator():
my_list = list(values)
i = 0
while(True):
yield my_list[i]
i += 1
if i == len(my_list):
i = 0
return seq_generator
```
The i += 1 is missing
Defining a procedure which return an index of item or -1 if the item not in list
def ser(a,b):
for j in a:
if j == b:
return (a.index(b))
else:
return -1
print (ser([1,2,3],3))
It's always return me -1. If i cut the 'else' part, it works. So why ?
That is because the first time you do not match the condition in your loop you immediately return and leave your method. You need to re-think your logic here to determine what it is you want to do when you don't match. Ultimately, you want to continue looping until you have exhausted your checks.
So, simply set your return -1 outside of your loop. If you go through your entire loop, you have not found your match, so you can then return -1
def ser(a,b):
for j in a:
if j == b:
return (a.index(b))
return -1
print (ser([1,2,3],3))
Alternatively, the loop can be avoided by using in. So, you can actually re-write your method to this:
def ser(a, b):
if b in a:
return a.index(b)
return -1
You are checking to see if item b is in list a, if it is, return the index, otherwise return -1
To take the simplification further, you can actually set this in to a single line in your return:
def ser(a, b):
return a.index(b) if b in a else -1
The else block is executed after the first iteration does not fulfill j == b.
You're better off moving the else block to the for which executes if the item is not found after the for loop is exhausted:
def ser(a,b):
for j in a:
if j == b:
return (a.index(b))
else: # or put default return on this line
return -1
More importantly, You could also check for containment using b in a without needing to iterate through the list.
In the first iteration of the for loop, it will test if the first element in the array is equal to b. It is not, so the code returns -1 immediately, without testing the other elements of the array.
For your case, the correct code is:
def ser(a,b):
for j in a:
if j == b:
return (a.index(b))
return -1
In this way, the code will try all elements in the array, and will return -1 if none of them is equal to b.
You need not to do anything fancy, simple one-liner will work:
def ser(a,b):
return a.index(b) if b in a else -1
# Example
my_list = [1, 2, 3, 4]
ser(my_list, 2)
# returns: 1
ser(my_list, 8)
# returns: -1
Can anyone please explain how to output the rightmost index from several most-same-values indexes in the list?
my function:
def last_index(xs,key):
i = 0
for i in range(len(xs)):
if xs[i] == key:
if i != len(xs):
return i
else:
return 'None'
for example,
xs = [3,4,5,6,4,4,5]
key = 4
the rightmost index output should be a single 5. But I got all three all them which are index 1,4,5.
Thanks for the help, and sorry I'm totally new.
what if the input as strings like:
xs=[True,True,True,False]
key = True
I believe the output is 2?
This simple solution should do:
def last_index(xs, key):
index = None
for i in xrange(len(xs)):
if xs[i] == key:
index = i # override index, result in the rightmost index of key
return index # returns None if key is not on the list
A more efficient way to do this is by iterating from the end to start and returning the index when key is found, in worst case - key is not found and we will iterate over the entire list.
Check out the more efficient version:
def last_index(xs, key):
index = None
for i in xrange(len(xs)-1, 0, -1): # iterate from last item to first
if xs[i] == key:
index = i
break # found the rightmost index, exit the loop
return index
Notice you should prefer using xrange over range (unless in python 3 where range equals to xrange), also to avoid an edge case when items involve different types see Andriy's answer.
You can try a function like this
def last_index(xs,key):
index = -1
for i in range(len(xs)):
if xs[i] == key:
index=i
if index!=-1:
return index
else:
return "none"
This will get the last index that matches your key. If there is none will return "none".
This should do the trick:
def last_index(xs,key):
index = -1
for i in range(len(xs)):
if xs[i] != key:
continue
else:
index = i
return index if index != -1 else 'None'
Traverse xs in reverse order and return first matched value, with reversed function:
def last_index(xs,key):
for i in reversed(range(len(xs))):
if xs[i] == key:
return i
xs = [3,4,5,6,4,4,5]
key = 4
print last_index(xs, key) # output: 5
xs=[True,True,True,False]
key = True
print last_index(xs, key) # output: 2
print last_index(xs, 2) # output: None
NOTE#1
You can use xrange instead of range it would give you better performace and won't be deprecated since python3, see Should you always favor xrange() over range()? for more info.
Your comparison may be improved by replacing
if xs[i] == key
to
if xs[i] == key and type(a) == type(b)
NOTE#2
To avoid bug when your 1 == True would return you index of True however you wanna index of 1 whicn not exist, compare result for both if conditions when xs and key have values below
xs=[True,True,True,False]
key = 1
See Strict comparison for more information about that behaviour.
You can reverse the list and then use .index():
index = xs[len(xs) - list(reversed(xs)).index(key)]
By the way, in your second list, True and False are booleans, not strings.
Iterate from behind like this:
def last_index(xs,key):
i= len(xs)-1
while i >=0:
if xs[i] == key:
return i
i -= 1
This way if the key does not exist the function will return the none value