Array Splitting in Python With Specific Input - python

If you are given two arrays as an input in the same line such as
[4,2,1,5,7],[4,1,2,3,5,7,1,2,7]
Is it possible to create separate arrays out of the above input?
arr1 = [4,2,1,5,7]
arr2 = [4,1,2,3,5,7,1,2,7]
I tried to use split(',') but since they are used in the actual arrays this does not work.
The length of the arrays can vary and the example above is just a sample.
Any help would be appreciated!

I would suggest "disguising" the input as a well-formed list by adding the outer brackets and then using literal_eval:
import ast
s = "[4,2,1,5,7],[4,1,2,3,5,7,1,2,7]"
parts = ast.literal_eval("[" + s + "]")
#[[4, 2, 1, 5, 7], [4, 1, 2, 3, 5, 7, 1, 2, 7]]
Or do not add anything and treat the input as a tuple of lists:
parts = ast.literal_eval(s)
#([4, 2, 1, 5, 7], [4, 1, 2, 3, 5, 7, 1, 2, 7])

This isn't the easy way, but if the goal is to learn to manipulate strings and lists, you can actually parse this the hard way as a stream of characters.
a = "[4,2,1,5,7],[45,1,2,3,5,7,100,2,7]"
l = []
current_n = ''
current_l = None
for c in a:
if c == '[':
current_l = []
elif c == ",":
if current_l is not None:
current_l.append(int(current_n))
current_n = ''
elif c.isdigit():
current_n += c
elif c == "]":
current_l.append(int(current_n))
l.append(current_l)
current_n = ''
current_l = None
l1, l2 = l
print(l1, l2)
# [4, 2, 1, 5, 7] [45, 1, 2, 3, 5, 7, 100, 2, 7]
Not something you would typically do, but a good exercise and it's simplicity should make is quite fast.

What you have there, once converted from a string using eval, is a 2-element tuple containing two lists. (The outer round parentheses are not mandatory in this situation.)
You could unpack it into two variables as follows:
str = '[4,2,1,5,7],[4,1,2,3,5,7,1,2,7]'
arr1, arr2 = eval(str)
Note: if the input string could derive from third-party input (for example in a server application) then eval should not be used for security reasons because it can allow for execution of arbitrary code, and ast.literal_eval should be used instead. (See separate answer by DYZ.) This will also return a 2-tuple of lists in the case of the input shown above, so the unpacking using var1, var2 = ... is unaffected.

Related

Sort a list from an index to another index [duplicate]

This question already has answers here:
Sort a part of a list in place
(3 answers)
Closed 3 years ago.
Suppose I have a list [2, 4, 1, 3, 5].
I want to sort the list just from index 1 to the end, which gives me [2, 1, 3, 4, 5]
How can I do it in Python?
(No extra spaces would be appreciated)
TL;DR:
Use sorted with a slicing assignment to keep the original list object without creating a new one:
l = [2, 4, 1, 3, 5]
l[1:] = sorted(l[1:])
print(l)
Output:
[2, 1, 3, 4, 5]
Longer Answer:
After the list is created, we will make a slicing assignment:
l[1:] =
Now you might be wondering what does [1:], it is slicing the list and starts from the second index, so the first index will be dropped. Python's indexing starts from zero, : means get everything after the index before, but if it was [1:3] it will only get values that are in between the indexes 1 and 3, let's say your list is:
l = [1, 2, 3, 4, 5]
If you use:
print(l[1:])
It will result in:
[2, 3, 4, 5]
And if you use:
print(l[1:3])
It will result in:
[2, 3]
About slicing, read more here if you want to.
And after slicing we have an equal sign =, that just simply changes what's before the = sign to what's after the = sign, so in this case, we use l[1:], and that gives [2, 3, 4, 5], it will change that to whatever is after the = sign.
If you use:
l[1:] = [100, 200, 300, 400]
print(l)
It will result in:
[1, 100, 200, 300, 400]
To learn more about it check out this.
After that, we got sorted, which is default builtin function, it simple sorts the list from small to big, let's say we have the below list:
l = [3, 2, 1, 4]
If you use:
print(sorted(l))
It will result in:
[1, 2, 3, 4]
To learn more about it check this.
After that we come back to our first topic about slicing, with l[1:], but from here you know that it isn't only used for assignments, you can apply functions to it and deal with it, like here we use sorted.
Maybe temporarily put something there that's smaller than the rest? Should be faster than the other solutions. And gets as close to your "No extra spaces" wish as you can get when using sort or sorted.
>>> tmp = l[0]
>>> l[0] = float('-inf')
>>> l.sort()
>>> l[0] = tmp
>>> l
[2, 1, 3, 4, 5]
Benchmarks
For the example list, 1,000,000 iterations (and mine of course preparing that special value only once):
sort_u10 0.8149 seconds
sort_chris 0.8569 seconds
sort_heap 0.7550 seconds
sort_heap2 0.5982 seconds # using -1 instead of -inf
For 50,000 lists like [int(x) for x in os.urandom(100)]:
sort_u10 0.4778 seconds
sort_chris 0.4786 seconds
sort_heap 0.8106 seconds
sort_heap2 0.4437 seconds # using -1 instead of -inf
Benchmark code:
import timeit, os
def sort_u10(l):
l[1:] = sorted(l[1:])
def sort_chris(l):
l = l[:1] + sorted(l[1:])
def sort_heap(l, smallest=float('-inf')):
tmp = l[0]
l[0] = smallest
l.sort()
l[0] = tmp
def sort_heap2(l):
tmp = l[0]
l[0] = -1
l.sort()
l[0] = tmp
for _ in range(3):
for sort in sort_u10, sort_chris, sort_heap, sort_heap2, sort_rev:
number, repeat = 1_000_000, 5
data = iter([[2, 4, 1, 3, 5] for _ in range(number * repeat)])
# number, repeat = 50_000, 5
# data = iter([[int(x) for x in os.urandom(100)] for _ in range(number * repeat)])
t = timeit.repeat(lambda: sort(next(data)), number=number, repeat=repeat)
print('%10s %.4f seconds' % (sort.__name__, min(t)))
print()
Use sorted with slicing:
l[:1] + sorted(l[1:])
Output:
[2, 1, 3, 4, 5]
For the special case that you actually have, according to our comments:
Q: I'm curious: Why do you want this? – Heap Overflow
A: I'm trying to make a next_permutation() in python – nwice13
Q: Do you really need to sort for that, though? Not just reverse? – Heap Overflow
A: Yup, reverse is ok, but I just curious to ask about sorting this way. – nwice13
I'd do that like this:
l[1:] = l[:0:-1]
You can define your own function in python using slicing and sorted and this function (your custom function) should take start and end index of the list.
Since list is mutable in python, I have written the function in such a way it doesn't modify the list passed. Feel free to modify the function. You can modify the list passed to this function to save memory if required.
def sortedList(li, start=0, end=None):
if end is None:
end = len(li)
fi = []
fi[:start] = li[:start]
fi[start:end] = sorted(li[start:end])
return fi
li = [2, 1, 4, 3, 0]
print(li)
print(sortedList(li, 1))
Output:
[2, 1, 4, 3, 0]
[2, 0, 1, 3, 4]

converting lists in list to a list only containing integers using recursion

One of the questions I had on an exam was to write a python program only using recursion (no loops were allowed).
The goal was to convert a list which contained both integers and lists (which also only contained integers) and make a single list only containing these integers.
Everything works fine in the code below until it encounters a list: after this it just stops.
The fix has to be fairly simple but I just can't find it.
a = [1,5,2,[3,4],6]
def list_in_list(l, i = 0):
if i >= len(l) - 1:
return [l[i]] if type(l[i]) == int else list_in_list(l[i], i=0)
elif type(l[i]) == list:
return list_in_list(l[i],i=0)
return [l[i]] + list_in_list(l, i+1)
print(list_in_list(a))
This works for any level of any level of nested lists:
a = [1,5,2,[3, [4, 7]],6]
def flatten(lst):
if not lst:
return []
first, rest = lst[0], lst[1:]
if isinstance(first, list):
return flatten(first) + flatten(rest)
else:
return [first] + flatten(rest)
print(flatten(a))
Prints:
[1, 5, 2, 3, 4, 7, 6]
The other answer did it without a loop as your question asked but I just wanted to make a few points.
1) Don't using type such as type(a) == str. Use isinstance(a, str). If you want to know why see What are the differences between type() and isinstance()?
2) I made the comment above and I know this is from exam, but this type of problem is inefficient to do with just pure recursion. Recursion is useful for a problem like this (I have done stuff like this a lot with JSON) but using pure recursion without using a loop takes up excessive memory and more time:
a = [1,5,2,[3, [4, 7]],6]
def list_in_list(l):
result = []
for element in l:
if isinstance(element, list):
result += list_in_list(element)
else:
result.append(element)
return result
print(list_in_list(a)) # [1, 5, 2, 3, 4, 7, 6]
Since you specified the [python-3x] tag, let's take advantage of some Python 3 features. Also, let's make the flatten() function have a single point of return instead of three:
example = [[8, 3, [2, 4, 1, [9, [7, 5, 6, [0]]]]]]
def flatten(array):
if array:
first, *array = array
if array:
array = flatten(array)
array = ([first] if isinstance(first, int) else flatten(first)) + array
return array
print(flatten(example))
We also save some recursions by doing an empty list check on the remainder of the list to decide if it's worth recuring.
USAGE
> python3 test.py
[8, 3, 2, 4, 1, 9, 7, 5, 6, 0]
>

Sum of certain items in a list

I'm working on a probability-related problem. I need to sum only specific items on a certain list.
I've tried using "for" functions and it hasn't worked. I'm looking for a way to select items based on their positions on the list, and summing them.
You can use operator.itemgetter to select only certian index’s in a list or keys in a dict.
from operator import itemgetter
data = [1, 2, 3, 4, 5, 6, 7, 8]
get_indexes = itemgetter(2, 5, 7)
#this will return indexes 2, 5, 7 from a sequence
sum(get_indexes(data)) #3+6+8
#returns 17
That example is for lists but you can use itemgetter for dict keys too just use itemgetter('key2', 'key5', 'key7')({some_dict})
To get only even or odd indexes use slicing not enumerate and a loop it’s much more efficient and easier to read:
even = sum(data[::2])
odd = sum(data[1::2])
You can also use filter but I wouldn’t suggest this for getting by index:
sum(filter(lambda n: data.index(n) % 2 == 0, data))
You really should have put more into your question, but:
stuff = [1, 2, 3, 4, 5, 6, 7, 8]
# sum the numbers that have even indices:
funny_total = sum([x for i, x in enumerate(stuff) if i % 2 == 0 ])
funny_total
# 16
That should get you started. An approach with a for loop would have worked, as well. You just likely have a bug in your code.
stuff = [1, 2, 3, 4, 5, 6, 7, 8]
indices_to_include = [1, 3, 4, 5, 6]
funny_total = 0
for i, x in enumerate(stuff):
if i in indices_to_include:
funny_total += x
You could also:
def keep_every_third(i):
return i % 3 == 0
# variable definitions as above...
for i, x in enumerate(stuff):
if keep_every_third(i):
# do stuff

python - Comparing two lists to see if one occurs in another consecutively

I've been trying to make a function that can take two lists of any size (say, list A and list B) and sees if list B occurs in list A, but consecutively and in the same order. If the above is true, it returns True, else it'll return False
e.g.
A:[9,0,**1,2,3,4,5,6,**7,8] and B:[1,2,3,4,5,6] is successful
A:[1,2,0,3,4,0,5,6,0] and B:[1,2,3,4,5,6] is unsuccessful.
A:[1,2,3,4,5,6] and B [6,5,3,2,1,4] fails because despite having the same
numbers, they aren't in the same order
I've tried doing this using nested loops so far and am a bit confused as to where to go
Just try this:
L1 = [9,0,1,2,3,4,5,6,7,8]
L2 = [1,2,3,4,5,6]
c = 0
w = 0
for a in range(len(L2)):
for b in range(w+1, len(L1)):
if L2[a] == L1[b]:
c = c+1
w = b
break
else:
c = 0
if c == len(L2):
print('yes')
break
Here you check if the element of l2 is in l1 and if so breaks the first loops remember where you left and of the next element of l2 is the same as the next element of l1 and so on.
And the last part is to check if this happened as much times as the length of l2. if so then you know that the statement is correct!
if your arrays are not huge and if you can find a way to map each element in your array to a string you can use:
list1 = [9,0,1,2,3,4,5,6,7,8]
list2 = [1,2,3,4,5,6]
if ''.join(str(e) for e in list2) in ''.join(str(e) for e in list1):
print 'true'
it just make two string from the lists and than use 'in' to find any accorence
Use any function
any(A[i:i+len(B)] == B for i in range(len(A) - len(B) + 1))
demo
i converted the entire list into a string and then found a substring of that string
the list when converted to a string it becomes
str(a)='[9,0,1,2,3,4,5,6,7,8]'
which when when we strip the string becomes
str(a).strip('[]')='9,0,1,2,3,4,5,6,7,8'
Now the problem just converted to
checking if there is a substring in the the string
so we can us the in operator to check the substring
The solution
a=[9,0,1,2,3,4,5,6,7,8]
b=[1,2,3,4,5,6]
print(str(b).strip('[]') in str(a).strip(']['))
testcase1
testcase2
Try this:
L1 = [9,2,1,2,0,4,5,6,7,8]
L2 = [1,2,3,4,5,6]
def sameorder(L1,L2):
for i in range(len(L1)-len(L2)+1):
if L1[i:len(L2)+i]==L2:
return True
return False
You can create sublists of a that can be analyzed:
def is_consecutive(a, b):
return any(all(c == d for c, d in zip(b, i)) for i in [a[e:e+len(b)] for e in range(len(a)-len(b))])
cases = [[[9, 0, 1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6]], [[1, 2, 0, 3, 4, 0, 5, 6, 0], [1, 2, 3, 4, 5, 6]], [[1, 2, 3, 4, 5, 6], [6, 5, 3, 2, 1, 4]]]
final_cases = {"case_{}".format(i):is_consecutive(*a) for i, a in enumerate(cases, start=1)}
Output:
{'case_3': False, 'case_2': False, 'case_1': True}

Compare Sequences Python

Is there a way in python to compare 2 sequences in lists even if they are not normalized (i think this is the right word). For example:
a = [1,1,2,3,3,1,5]
b = [2,3,3,1,5,1,1]
c = [1,1,1,2,3,3,5]
a == b should return True as they contain the same sequence just from a different starting point.
c == a should return False as although they contain the same elements, they do not contain the same sequence
The only thing I can thing of is rather inelegant. I would compare 2 lists and if they are not equal, shift the last element of the list to the front and compare again. Repeat this until I have shifted the entire list once. However, I will be working with some very large lists so this will be very inefficient
This might be more efficient than shifting elements:
>>> a = [1, 1, 2, 3, 3, 1, 5]
>>> b = [2, 3, 3, 1, 5, 1, 1]
>>> c = [1, 1, 1, 2, 3, 3, 5]
>>> astr, bstr, cstr = ["".join(map(str, x)) for x in (a, b, c)]
>>> astr in bstr*2
True
>>> cstr in astr*2
False
What it does is basically join the lists to strings and check if the first string is contained in the other 'doubled'.
Using strings is probably the fastest and should work for simple cases like in the OP. As a more general approach, you can apply the same idea to list slices, e.g.:
>>> any(idx for idx in range(len(a)) if (b*2)[idx:idx+len(a)] == a)
True

Categories