I'm trying to create a defined function (without list/string methods):
so far i have code to update at the given location, but I'm struggling to create code that can insert the variable at list location, and move existing elements over. I want the output to be ['a', 'b', 'c', 'd'] instead of ['a', 'b', 'c', 'e'].
Please note: this is for study, I do want to learn I'd prefer to be pointed in the right direction, not just provided the answer.
def insert(list1, var, i):
counter = 0
for i in my_list:
if counter == i:
my_list[i] = var
counter += 1
return list1
list = ['a', 'b', 'd', 'e']
func = insert(list, 'c', 2)
i've been working on code overnight (sorry for such a late reply i was on a plane)
I've come up with this code and I think it works, even though it's a little crude. can anyone give some input as to whether they see this being an issue or not?
def insert(list1, var, i):
new_list = []
counter = -1
for index in (list1[:-1]):
counter += 1
new_list.append(index)
if counter == (i-1):
new_list.append(var)
return new_list
list2= ['a', 'b', 'c', 'd', 'e', 'f', 'h']
func = insert(list2, 'g', 6)
print(func)
Using slicing, it's fairly easy to do this without use of any list methods at all. If you're using Python 3.5+ with additional unpacking generalizations, it even looks pretty:
def insert(list1, var, i):
# Reassign all elements at and beyond the insertion point to be the new
# value, and all but the last value previously there (so size is unchanged)
list1[i:] = var, *list1[i:-1]
or if mutating the caller's argument in place is bad, and you want to return a new value:
def insert(list1, var, i):
# Unpacking the components sliced as before to a brand new list
return [*list1[:i], var, *list1[i:-1]]
Without the unpacking generalizations, it's not quite as pretty, but still doable, either the argument mutating:
def insert(list1, var, i):
# Uglier without unpacking generalizations
list1[i], list1[i+1:] = var, list1[i:-1]
or new list constructing:
def insert(list1, var, i):
# An extra temporary without unpacking generalizations, but not ugly
return list1[:i] + [var] + list1[i:-1]
If you can slice or index, this won't help, but I wanted to point out the power of slice assignment and the new unpacking generalizations.
If you want a solution using only range and/or .append with no slicing:
def insert(list1, var, i):
for idx in range(len(list1)-1, i, -1):
list1[idx] = list1[idx-1]
list1[i] = var
or non-mutating:
def insert(list1, var, i):
newlist = []
for idx in range(i):
newlist.append(list1[idx])
newlist.append(var)
for idx in range(i, len(list1)-1):
newlist.append(list1[idx])
return newlist
def insert(list1, var, i):
new_list = list1[:i]
new_list.append(var)
new_list.extend(list1[i:])
return new_list
list = ['a', 'b', 'd', 'e']
func = insert(list, 'c', 2)
take the part of list1 up to i. This is called slicing
tack var onto new_list with append
tack on the rest of the list with extend
['a', 'b', 'c', 'd', 'e']
Related
Im trying to add a prefix to a list of strings in Python. The list of strings may contain multiple levels of nested lists.
Is there a way to loop through this list (and its nested lists), while keeping the structure?
nested for-loops became unreadable very quick, and did not seem to be the right approach..
list = ['a', 'b', ['C', 'C'], 'd', ['E', ['Ee', 'Ee']]]
for i in list:
if isinstance(i, list):
for a in i:
a = prefix + a
#add more layers of for loops
else:
i = prefix + i
desired outcome:
prefix = "#"
newlist = ['#a', '#b', ['#C', '#C'], '#d', ['#E', ['#Ee', '#Ee']]]
Thanks in advance!
You could write a simple recursive function
def apply_prefix(l, prefix):
# Base Case
if isinstance(l, str):
return prefix + l
# Recursive Case
else:
return [apply_prefix(i, prefix) for i in l]
l = ['a', 'b', ['C', 'C'], 'd', ['E', ['Ee', 'Ee',]]]
print(apply_prefix(l, "#"))
# ['#a', '#b', ['#C', '#C'], '#d', ['#E', ['#Ee', '#Ee']]]
This will use recursion:
a = ['a', 'b', ['C', 'C'], 'd', ['E', ['Ee', 'Ee',]]]
def insert_symbol(structure, symbol='#'):
if isinstance(structure, list):
return [insert_symbol(sub_structure) for sub_structure in structure]
else:
return symbol + structure
print(insert_symbol(a))
>>> ['#a', '#b', ['#C', '#C'], '#d', ['#E', ['#Ee', '#Ee']]]
You can use a recursive code like this!, Try it, and if you have questions you can ask me
def add_prefix(input_list):
changed_list = []
for elem in input_list:
if isinstance(elem, list):
elem = add_prefix(elem)
changed_list.append(elem)
else:
elem = "#" + elem
changed_list.append(elem)
return changed_list
Maybe you can use a function to do it recursively.
list_example = ['a', 'b', ['C', 'C'], 'd', ['E', ['Ee', 'Ee']]]
def add_prefix(p_list, prefix):
for idx in range(len(p_list)):
if isinstance(p_list[idx], list):
p_list[idx] = add_prefix(p_list[idx], prefix)
else:
p_list[idx] = prefix + p_list[idx]
return p_list
add_prefix(list_example, '#')
edit: I see now someone has posted the almost same thing.
btw. it is considered bad practice to name a list list, since it is also a typename in python. Might result in unwanted behaviour
I am trying to reverse a list's order by finding three bugs in this function. This function is supposed to reverse the first and last elements of a list, the second and second to last elements, and so on. I believe I found two, but am having trouble fixing the line of list[j] = y.
def reverse(list):
"""Reverses elements of a list."""
for i in range(len(list)):
j = len(list) - i
x = list[i]
y = list[j-1]
list[i] = x
list[j] = y
l = ['a', 'b', 'c', 'd', 'e']
reverse(l)
print(l)
Homework I suspect...
But - we all need a break from homework. By looping over the whole list you're reversing it twice.
def reverse(list):
"""Reverses elements of a list."""
for i in range(len(list)/2):
j = i + 1
x = list[i]
y = list[-j]
list[-j] = x
list[i] = y
l = ['a', 'b', 'c', 'd', 'e']
l=reverse(l)
print(l)
resulting in
['e', 'd', 'c', 'b', 'a']
You have a couple problems. Your first problem is that you use list[j] = y instead of list[j-1] = x. You defined y correctly with j-1, but you should be changing list[j-1] to the other one, x. Another problem is that you are going from the beginning of the list all the way to the end. Once you get to more than half way through the list, you are undoing your work. You also don't need to use len(list)-i because you can just use -i. Here is the updated code:
def reverse(seq):
"""Reverses elements of a list."""
for i in range(len(seq)//2):
x = seq[i]
y = seq[-i-1]
seq[i] = y
seq[-i-1] = x
l = ['a', 'b', 'c', 'd', 'e']
reverse(l)
print(l)
Output:
['e', 'd', 'c', 'b', 'a']
You don't even need to define x and y. Instead, do this:
def reverse(seq):
"""Reverses elements of a list."""
for i in range(len(list)//2):
seq[i], seq[-i-1] = seq[-i-1], seq[i]
I also changed your naming. There's probably a better name than seq, but list is unacceptable because it conflicts with the built-in type.
Use this code:
l = ['a', 'b', 'c', 'd', 'e']
l=l[::-1]
print(l)
Why you want to complicate this simple construction? Or if you don't wanna do this on that way, try to use:
l.reverse()
function. Python has a lot of functions ready to use.
This question already has answers here:
How can I use `return` to get back multiple values from a loop? Can I put them in a list?
(2 answers)
Closed 4 months ago.
I want to know how to return values without breaking a loop in Python.
Here is an example:
def myfunction():
list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
print(list)
total = 0
for i in list:
if total < 6:
return i #get first element then it breaks
total += 1
else:
break
myfunction()
return will only get the first answer then leave the loop, I don't want that, I want to return multiple elements till the end of that loop.
How can resolve this, is there any solution?
You can create a generator for that, so you could yield values from your generator (your function would become a generator after using the yield statement).
See the topics below to get a better idea of how to work with it:
Generators
What does the "yield" keyword do?
yield and Generators explain's
An example of using a generator:
def simple_generator(n):
i = 0
while i < n:
yield i
i += 1
my_simple_gen = simple_generator(3) # Create a generator
first_return = next(my_simple_gen) # 0
second_return = next(my_simple_gen) # 1
Also you could create a list before the loop starts and append items to that list, then return that list, so this list could be treated as list of results "returned" inside the loop.
Example of using list to return values:
def get_results_list(n):
results = []
i = 0
while i < n:
results.append(i)
i += 1
return results
first_return, second_return, third_return = get_results_list(3)
NOTE: In the approach with list you have to know how many values your function would return in results list to avoid too many values to unpack error
Using a generator is a probable way:
def myfunction():
l = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
total = 0
for i in l:
if total < 6:
yield i #yields an item and saves function state
total += 1
else:
break
g = myfunction()
Now you can access all elements returned with yield i by calling next() on it:
>>> val = next(g)
>>> print(v)
a
>>> v = next(g)
>>> print(v)
b
Or, in a for loop by doing:
>>> for returned_val in myfunction():
... print(returned_val)
a
b
c
d
e
f
What you want is most easily expressed with list slicing:
>>> l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
>>> l[:6]
# ['a', 'b', 'c', 'd', 'e', 'f']
Alternatively create another list which you will return at the end of the function.
def myfunction():
l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
ret = []
total = 0
for i in l:
if total < 6:
total += 1
ret.append(i)
return ret
myfunction()
# ['a', 'b', 'c', 'd', 'e', 'f']
A yield statement to create a generator is what you want.
What does the "yield" keyword do in Python?
Then use the next method to get each value returned from the loop.
var = my_func().next()
I'm not sure about the term of this technique. I want to create a list where if I try to access an element outside the list range, the list would "loop itself". Example of the behaviour I want to achieve:
>>> musical_scale = ['C', 'D', 'E', 'F', 'G', 'A', 'B']
>>> musical_scale[2]
E
>>> musical_scale[7]
C
>>> musical_scale[-1]
B
I guess I could write a class which does this, but I thought there might be a more correct way of doing it.
Creating a subclass of List would be a pretty useful way of doing this. Something like this, perhaps:
class modList(list):
def __getitem__(self, i):
if len(self) == 0:
raise IndexError # Or do something else if you want, like return []
i = i % len(self) # self.__len__() works also
return super(modList, self).__getitem__(i) # In Python 3, super().__getitem__(i)
If you want to do slicing, it's a little more complicated, but similar. Found this while looking through StackOverflow:
def __getitem__(self, i):
if isinstance(i, int):
if len(self) == 0:
raise IndexError
i = i % len(self)
return super(modList, self).__getitem__(i) # list.__getitem__(i) works too
elif isinstance(i, slice):
if len(self) == 0:
return []
start = i.start % len(self)
stop = i.stop % len(self)
step = i.step
return super(modList, self).__getItem__(slice(start, stop, step))
else:
raise TypeError("invalid index")
Though this slice modding could give you a situation like [3:2], which will return an empty list. Basically slicing is hard and you'll need to decide how you want to implement it, but hopefully this is a start.
http://www.stackoverflow.com/questions/3911483/python-slice-how-to-i-know-the-python-slice-but-how-can-i-use-built-in-slice-ob
(Thanks #JonClements for all the suggestions in chat.)
EDIT: now we have some handlers for if you have an empty list. #wim suggests raising an error for single accesses, and returning [] for slices. Really it's up to you what you want to do but that seemed sensible to me, so that's what I've included in this answer.
EDIT EDIT: if you're using Python 2.x, I believe you also need to override __getslice__.
Use the modulus operator % to "loop back" after indexing past the end of the list
>>> musical_scale = ['C', 'D', 'E', 'F', 'G', 'A', 'B']
def getValue(x, l):
return l[x % len(l)]
>>> getValue(0, musical_scale)
'C'
>>> getValue(9, musical_scale)
'E'
In case you'd like to extend the list class to handle getting and setting...
class UnsatList(list):
def __getitem__(self, index):
try:
return list.__getitem__(self, index % len(self))
except ZeroDivisionError:
raise IndexError('list assignment index out of range')
def __setitem__(self, index, value):
try:
return list.__setitem__(self, index % len(self), value)
except ZeroDivisionError:
raise IndexError('list assignment index out of range')
if __name__ == '__main__':
test_list = UnsatList([1,2,3,4])
assert test_list[0] == 1
assert test_list[4] == 1
assert test_list[8] == 1
assert test_list[-8] == 1
test_list[4] = 5
assert test_list[0] == 5
assert test_list[4] == 5
assert test_list[8] == 5
assert test_list[-8] == 5
Here is a solution, but remember The Zen of Python: Flat is better than nested.
You can write for example:
a = [1, [2]]
a[1].append(a)
Then your list a has "infinite depth", and you can loop on these two elements by repeatedly doing:
a[0]
a = a[1]
You can't do that without playing with depth, because unlike e.g. Lisp, Python lists are varying-length arrays, not linked-lists.
Here are little functions to do the whole stuff automatically:
def loopify(a):
r = b = []
for x in a:
b.append(x)
b.append([])
c = b
b = b[1]
c[1] = r
return r
def loopgen(a):
while True:
yield a[0]
a = a[1]
Then with your example:
musical_scale = loopify(['C', 'D', 'E', 'F', 'G', 'A', 'B'])
g = loopgen(musical_scale)
[next(g) for i in range(20)]
This yields:
['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'A']
Of course, it's also possible to use mod or to keep track of the current index and reseting it to 0 when you overflow your list. Other answers show you how to do that.
Also notice that the linked-list approach does not permit random access, only sequential access (thus the generator above). If you need to access you list randomly, modular arithmetic is the most appropriate way.
Let's say I have a Python list that looks like this:
list = [ a, b, c, d]
I am looking for the most efficient way performanse wise to get this:
list = [ a, a, a, a, b, b, b, c, c, d ]
So if the list is N elements long then the first element is cloned N-1 times, the second element N-2 times, and so forth...the last element is cloned N-N times or 0 times. Any suggestions on how to do this efficiently on large lists.
Note that I am testing speed, not correctness. If someone wants to edit in a unit test, I'll get around to it.
pyfunc_fastest: 152.58769989 usecs
pyfunc_local_extend: 154.679298401 usecs
pyfunc_iadd: 158.183312416 usecs
pyfunc_xrange: 162.234091759 usecs
pyfunc: 166.495800018 usecs
Ignacio: 238.87629509 usecs
Ishpeck: 311.713695526 usecs
FabrizioM: 456.708812714 usecs
JohnKugleman: 519.239497185 usecs
Bwmat: 1309.29429531 usecs
Test code here. The second revision is trash because I was rushing to get everybody tested that posted after my first batch of tests. These timings are for the fifth revision of the code.
Here's the fastest version that I was able to get.
def pyfunc_fastest(x):
t = []
lenList = len(x)
extend = t.extend
for l in xrange(0, lenList):
extend([x[l]] * (lenList - l))
Oddly, a version that I modified to avoid indexing into the list by using enumerate ran slower than the original.
>>> items = ['a', 'b', 'c', 'd']
>>> [item for i, item in enumerate(items) for j in xrange(len(items) - i)]
['a', 'a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd']
First we use enumerate to pull out both indexes and values at the same time. Then we use a nested for loop to iterate over each item a decreasing number of times. (Notice that the variable j is never used. It is junk.)
This should be near optimal, with minimal memory usage thanks to the use of the enumerate and xrange generators.
How about this - A simple one
>>> x = ['a', 'b', 'c', 'd']
>>> t = []
>>> lenList = len(x)
>>> for l in range(0, lenList):
... t.extend([x[l]] * (lenList - l))
...
>>> t
['a', 'a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd']
>>>
Lazy mode:
import itertools
l = ['foo', 'bar', 'baz', 'quux']
for i in itertools.chain.from_iterable(itertools.repeat(e, len(l) - i)
for i, e in enumerate(l)):
print i
Just shove it through list() if you really do need a list instead.
list(itertools.chain.from_iterable(itertools.repeat(e, len(l) - i)
for i, e in enumerate(l)))
My first instinct..
l = ['a', 'b', 'c', 'd']
nl = []
i = 0
while len(l[i:])>0:
nl.extend( [l[i]]*len(l[i:]) )
i+=1
print nl
The trick is in using repeat from itertools
from itertools import repeat
alist = "a b c d".split()
print [ x for idx, value in enumerate(alist) for x in repeat(value, len(alist) - idx) ]
>>>['a', 'a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd']
Use a generator: it's O(1) memory and O(N^2) cpu, unlike any solution that produces the final list which uses O(N^2) memory and cpu. This means it'll be massively faster as soon as the input list is large enough that the constructed list fills memory and swapping starts. It's unlikely you need to have the final list in memory unless this is homework.
def triangle(seq):
for i, x in enumerate(seq):
for _ in xrange(len(seq) - i - 1):
yield x
To create that new list, list = [ a, a, a, a, b, b, b, c, c, d ] would require O(4n) = O(n) time since for every n elements, you are creating 4n elements in the second array. aaronasterling gives that linear solution.
You could cheat and just not create the new list. Simply, get the index value as input. Divide the index value by 4. Use the result as the index value of the original list.
In pseudocode:
function getElement(int i)
{
int trueIndex = i / 4;
return list[trueIndex]; // Note: that integer division will lead us to the correct index in the original array.
}
fwiw:
>>> lst = list('abcd')
>>> [i for i, j in zip(lst, range(len(lst), 0, -1)) for _ in range(j)]
['a', 'a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd']
def gen_indices(list_length):
for index in range(list_length):
for _ in range(list_length - index):
yield index
new_list = [list[i] for i in gen_indices(len(list))]
untested but I think it'll work