List and Integer Python - python

I am trying to write a code that changes the position of an integer inside a list (basically swaps the position with another integer)
I have tried to use all logic, but still can't understand why my code is messing up:
SpecialNum = 10
def number_move(move_number):
for elements in range(len(move_number)):
if ( SpecialNum != move_number[-1]):
x = move_number.index(SpecialNum)
y = move_number.index(SpecialNum)+1
move_number[y], move_number[x] = move_number[x], move_number[y]
return (move_number)
the output should be:
[1,2,3,10,4,5,6]
>>>[1,2,3,4,10,5,6]
but output comes as:
[1,2,3,4,5,6,10]

Assuming your actual indentation looks like this:
SpecialNum = 10
def number_move(move_number):
for elements in range(len(move_number)):
if ( SpecialNum != move_number[-1]):
x = move_number.index(SpecialNum)
y = move_number.index(SpecialNum)+1
move_number[y], move_number[x] = move_number[x], move_number[y]
return move_number
… the problem is that you're swapping the 10 to the right over and over in a loop, until it reaches the very end.
If that isn't what you want, why do you have the for elements in range(len(move_number)) in the first place? Just take it out, and it will only get swapped right once.
As a side note, you rarely need range(len(eggs)); you can just do for egg in eggs (or, if you need the index along with the actual object, for index, egg in enumerate(eggs)).
Also, you've got a whole lot of extra parentheses that aren't needed, and make the code harder to read.
Meanwhile, every call to index has to search the entire list to find your object's position; if you already know the position, it's better to just use it. Not only is it a lot faster, and simpler, it's also more robust—if there are two elements of the list with the same value, index can only find the first one. In your case, there's no obvious way around using index, but at least you can avoid calling it twice.
Putting that together:
SpecialNum = 10
def number_move(move_number):
x = move_number.index(SpecialNum)
y = x + 1
if y != len(move_number):
move_number[y], move_number[x] = move_number[x], move_number[y]
Finally, I said there's no obvious way around using index… but is there a non-obvious way? Sure. If you're going to call index repeatedly on the same object, we can make the last-found index part of the interface to the function, or we can even store a cache inside the function. The simplest way to do this is to turn the whole thing into a generator. A generator that mutates its arguments can be kind of confusing, so let's make it return copies instead. And finally, to make it customizable, let's take a parameter so you can specify a different SpecialNum than 10.
SpecialNum = 10
def number_move(move_number, special_num=SpecialNum):
for x, element in reversed(list(enumerate(move_number))):
if element == special_num:
while x+1 < len(move_number):
move_number = (move_number[:x] +
[move_number[x+1], move_number[x]] +
move_number[x+2:])
yield move_number
x += 1
Now, it'll move all of the 10s to the end, one step at a time. Like this:
>>> n = [1, 10, 2, 3, 10, 4, 5, 6]
>>> for x in number_move(n):
... print(x)
[1, 10, 2, 3, 4, 10, 5, 6]
[1, 10, 2, 3, 4, 5, 10, 6]
[1, 10, 2, 3, 4, 5, 6, 10]
[1, 2, 10, 3, 4, 5, 6, 10]
[1, 2, 3, 10, 4, 5, 6, 10]
[1, 2, 3, 4, 10, 5, 6, 10]
[1, 2, 3, 4, 5, 10, 6, 10]
[1, 2, 3, 4, 5, 6, 10, 10]
[1, 2, 3, 4, 5, 6, 10, 10]

You don't need the for loop :)
def number_move(move_number):
x = move_number.index(SpecialNum)
y = move_number.index(SpecialNum)+1
move_number[y], move_number[x] = move_number[x], move_number[y]
Alternative:
>>> def number_move(m, i):
num = m.pop(i)
m.insert(i+1, num)
return m
>>> l = number_move([1,2,3,10,4,5,6], 3)
>>> l
[1, 2, 3, 4, 10, 5, 6]

Related

How to insert an element in a list after using del in python

def my_len(lst):
mylen = 0
for i in lst:
mylen += 1
return mylen
def insrt(lst, what, where):
length = my_len(lst)
tmp = [0] * (length + 1)
for i in range(0, where):
tmp[i] = lst[i]
tmp[where] = what
for i in range(where, length + 1):
tmp[i] = lst[i - 1]
return tmp
def move(lst, from_index, to_index):
a = lst[from_index]
del lst[from_index]
insrt(lst, a, to_index)
return lst
my intention was if the original list input is [1, 2, 3, 4, 5, 6], then print(move(lst, 3, 1) will give [1, 4, 2, 3, 5, 6], but my code gives [1, 2, 3, 5, 6]. Please explain how to fix it.
NB. See the end of the answer for an alternative interpretation of the move
If you want the item to end up in the defined to_index, you can simply use list.insert and list.pop:
def move(lst, from_index, to_index):
lst.insert(to_index, lst.pop(from_index))
examples:
l = [1, 2, 3, 4, 5, 6]
move(l, 1, 3)
print(l)
[1, 3, 4, 2, 5, 6]
l = [1, 2, 3, 4, 5, 6]
move(l, 3, 1)
print(l)
[1, 4, 2, 3, 5, 6]
l = [1, 2, 3, 4, 5, 6]
move(l, 3, 3)
print(l)
[1, 2, 3, 4, 5, 6]
NB. your function should probably either return a copy, or modify the list in place, not both. (here I chose to modify in place, see below for a variant).
copy variant:
def move(lst, from_index, to_index):
lst = lst.copy()
lst.insert(to_index, lst.pop(from_index))
return lst
l = [1, 2, 3, 4, 5, 6]
l2 = move(l, 1, 3)
print(l)
[1, 2, 3, 4, 5, 6]
print(l2)
[1, 3, 4, 2, 5, 6]
alternative interpretation: move to the position as defined before the move
In this different interpretation, the item moves to the position as defined if the insertion occured before the deletion.
In this case we need to correct the insertion position if it is greater than the initial position.
We can take advantage of the boolean/integer equivalence by subtracting to_index>from_index (1 if True, 0 otherwise)
def move(lst, from_index, to_index):
lst.insert(to_index-(to_index>from_index), lst.pop(from_index))
l = [1, 2, 3, 4, 5, 6]
move(l, 1, 3)
print(l)
# [1, 3, 2, 4, 5, 6]
This would work,
def move(lst, from_index, to_index):
new_lst = lst[:to_index] + [lst[from_index]] + [item for item in lst[to_index:]]
new_lst.pop(from_index + 1)
return new_lst
lst = [1, 2, 3, 4, 5, 6]
move(lst, 3, 1)
Output -
[1, 4, 2, 3, 5, 6]
The main problem you're seeing right now relates to how your insrt function works, as compared to how you're calling it.
The way you've written it, insert returns a new list containing the values from the old list plus one new value at a particular index. But when you call it in move, you seem to be expecting it to modify the given list in place. It doesn't do that. You need to change at least one of the two functions so that they match up. Either make insrt work in-place, or make move save the value returned by it rather than throwing it away.
Anyway, here's a very simple tweak to your move function that makes it work properly with the insrt function as you've shown it:
def move(lst, from_index, to_index):
a = lst[from_index]
del lst[from_index]
new_lst = insrt(lst, a, to_index) # save the new list after the insert
return new_lst # and then return it
I'd note that using del on the earlier line already modifies the lst passed in, so this design may not make a whole lot of sense. You might want to modify insrt to work in place instead, to be consistent. That would look something like this (which would work with your original move):
def insrt(lst, what, where):
length = my_len(lst)
lst.append(None) # add a dummy value at the end of the list
for i in range(length-1, where, -1): # iterate backwards from the end
lst[i] = lst[i - 1] # shift each value back
lst[where] = what # plug in the new value
# no return any more, this version works in place

How to add the first four digits in to another list

I want to take the first four digits from the already shuffled list and add them to a new list. I thought of append but all it does is return the whole list 4 times.
base = [1, 2, 3, 4, 5, 6]
random.shuffle(base)
correct = []
for i in range(4):
correct.append(base)
print(correct)
You can do it like this:
import random
base = [1, 2, 3, 4, 5, 6]
random.shuffle(base)
correct = base[:4]
print(correct)
This works because of list slicing
If for whatever reason you want to avoid using list slicing here is how you would do it in a way you originally tried.
base = [1, 2, 3, 4, 5, 6]
random.shuffle(base)
correct = []
for i in range(4):
correct.append(base[i])
print(correct)

How to modify a variable in a class function

So i got this function. It must not be changed!
class TestUnikati(unittest.TestCase):
def test_02_clean(self):
s = [5, 4, 1, 4, 4, 4, 4, 4, 3]
unique_array(s) #<-- calls the function
self.assertEqual(s, [5, 4, 1, 3])
Basically we test if the function clean() only returns a unique array. The variable s being the array.
This is my function that get's the messy array s and tries to return an array of no duplicate elements
def unique_array(s):
s=unique(s) #<-- this is a function that just returns a unique array
x=TestUnikati() #<-- I store the class in the x variable
[insert a way to push s to the "TestUnikati.test_02_clean.s"]
I tried many things. I've tried some experiments with globals() and locals() as well as many things with the x object variable but I don't seem to get it right.
I've tried to push it to the locals() of TestUnikati.test_02_clean.s with the x object. Is there a way to save it so the s in the class function will be over-ridden and the self.assertEqual(s, [5, 4, 1, 3]) will compare the 2 and pass it? Something like this:
x.test_02_clean.s=unique(s)
or
x.s=unique(s)
As others have stated, since test_02_clean() doesn't assign the results of its call of unique_array() to anything, it's expecting you to mutate the given list in place. Something like this:
def unique_array(s):
# Keep a set of all the items we've seen so far
seen = set()
# Index into list
i = 0
while i < len(s):
if s[i] in seen:
# Delete element from list if we've already seen it
del s[i]
else:
# Or else add it to our seen list and increment the index
seen.add(s[i])
i += 1
>>> s = [5, 4, 1, 4, 4, 4, 4, 4, 3]
>>> unique_array(s)
>>> s
[5, 4, 1, 3]
you must have to change your class variable to access globally like below:-
class TestUnikati(unittest.TestCase):
def test_02_clean(self):
self.s = [5, 4, 1, 4, 4, 4, 4, 4, 3]
unique(self.s) <-- calls the function
self.assertEqual(self.s, [5, 4, 1, 3])
and also to reassign it with shorted value you have to do like:-
self.s=unique(s)
You need a function unique() that changes the list in place, instead of returning a new list, e.g.
def unique(l):
count = {}
for entry in l:
count[entry] = count.get(entry, 0) + 1
for entry in l:
for _ in range(count[entry] - 1):
l.remove(entry)
This works in place:
a = [1, 1, 1, 3, 4, 2]
unique(a)
print(a)
>>> [1, 3, 4, 2]

insert top to bottom of list in python

I need to design a function that takes a list of int as a parameter and in that list we look at the last element it will have some value v, Then we take v cards from the top of the deck and put them above the bottom most card in the deck.
Here is an example:
>>> test_list = [1, 28, 3, 4, 27, 8, 7, 6, 5]
>>> insert_top_to_bottom(test_list)
>>> test_list = [8, 7, 6, 1, 28, 3, 4, 27, 5]
value = deck[-1] # value of the last element
I believe this will do
def insert_top_to_bottom(test_list, v):
return test_list[v : -1] + test_list[:v] + [test_list[-1]]
test_list = [1, 28, 3, 4, 27, 8, 7, 6, 5]
test_list = insert_top_to_bottom(test_list, 5)
print test_list
Here is my attempt, though you should next time show your own attempts.
Please note that I did not include any type of checking or avoiding errors. Just the basics.
def top_to_bottom(l):
last = l[len(l)-1]
move_these = l[:last]
move_these.append(l[len(l)-1])
del l[:last]
del l[len(l)-1]
l.extend(move_these)
I hope I could help.
EDIT
I didn't make it a one-line funtion so you can understand a bit better.
Since you asked, here is some more explanaition about slicing.
my_list[x:y] is a basic slice. This will output my_list from index x to (but excluding index y).
You can leave out either, which will just fill up that part as far as possible.
For example, using
my_list[:5]
will give you my_list from the beginning to (but excluding!) index 5.
If the list is [0, 1, 2, 3, 4, 5, 6], it will give [0, 1, 2, 3, 4]. If you want to get a list from a certain index until the end, you leave out the 'y' part. Like so:
my_list[3:]
Applying that on the list previously stated, you will get [3, 4, 5, 6].
I hope you understood! Comment if you have any more questions.

Contracting in python

I'm supposed to write a program that contracts a list. For example:
[1, 3, 4, 5, 1, 8, 6,]
has to be contracted into a list that looks like this:
[1, 5, 1, 8, 6, 6]
I don't have a clue on how to do this and was hoping that any of you could help me.
I'm given a list that looks like this l1 = [1, 3, 9, 1, 2, 7, 8] and i'm supposed to contract the list into a new list which consist of the old lists nondecending segments extremities. It is supposed to work with any list given.
Let me try to understand the question:
im given a list that looks like this l1 = [1, 3, 9, 1, 2, 7, 8] and im
supposed to contract the list by taking the first number then the next
bigest and the smallest after that and then the biggest again. This is
a school assignment and i have no idea on how to do this. english
isn't my first language and it's realy difficult to explain the
assignment :/
I supposed that "biggest" and "smallest" refer to local maxima and minima. So a number at index i is "biggest" if l[i-1] < l[i] and l[i] > l[i+1]. "Smallest" the other way around. So basically your are searching for the extrema of a function N -> N.
If this is what you want, this should help (considering that start and end points always are extrema):
#! /usr/bin/python3.2
def sign(x): return 0 if not x else x // abs(x)
def extrema (l):
return [l[0]] + [e for i, e in enumerate(l[:-1]) if i and sign(e-l[i-1])==sign(e-l[i+1])] + [l[-1]]
l1 = [1, 3, 9, 1, 2, 7, 8]
print (extrema (l1))

Categories

Resources