I'm working on a script where I have a list of tuples like ('1','2','3','4'). e.g.:
list = [('1','2','3','4'),
('2','3','4','5'),
('3','4','5','6'),
('4','5','6','7')]
Now I need to add '1234', '2345','3456' and '4567' respectively at the end of each tuple. e.g:
list = [('1','2','3','4','1234'),
('2','3','4','5','2345'),
('3','4','5','6','3456'),
('4','5','6','7','4567')]
Is it possible in any way?
Tuples are immutable and not supposed to be changed - that is what the list type is for.
However, you can replace each tuple using originalTuple + (newElement,), thus creating a new tuple. For example:
t = (1,2,3)
t = t + (1,)
print(t)
(1,2,3,1)
But I'd rather suggest to go with lists from the beginning, because they are faster for inserting items.
And another hint: Do not overwrite the built-in name list in your program, rather call the variable l or some other name. If you overwrite the built-in name, you can't use it anymore in the current scope.
Based on the syntax, I'm guessing this is Python. The point of a tuple is that it is immutable, so you need to replace each element with a new tuple:
list = [l + (''.join(l),) for l in list]
# output:
[('1', '2', '3', '4', '1234'),
('2', '3', '4', '5', '2345'),
('3', '4', '5', '6', '3456'),
('4', '5', '6', '7', '4567')]
As mentioned in other answers, tuples are immutable once created, and a list might serve your purposes better.
That said, another option for creating a new tuple with extra items is to use the splat operator:
new_tuple = (*old_tuple, 'new', 'items')
I like this syntax because it looks like a new tuple, so it clearly communicates what you're trying to do.
Using splat, a potential solution is:
list = [(*i, ''.join(i)) for i in list]
In Python, you can't. Tuples are immutable.
On the containing list, you could replace tuple ('1', '2', '3', '4') with a different ('1', '2', '3', '4', '1234') tuple though.
As other people have answered, tuples in python are immutable and the only way to 'modify' one is to create a new one with the appended elements included.
But the best solution is a list. When whatever function or method that requires a tuple needs to be called, create a tuple by using tuple(list).
I was going through some details related to tuple and list, and what I understood is:
Tuples are Heterogeneous collection data type
Tuple has Fixed length (per tuple type)
Tuple are Always finite
So for appending new item to a tuple, need to cast it to list, and do append() operation on it, then again cast it back to tuple.
But personally what I felt about the Question is, if Tuples are supposed to be finite, fixed length items and if we are using those data types in our application logics then there should not be a scenario to appending new items OR updating an item value in it.
So instead of list of tuples it should be list of list itself, Am I right on this?
list_of_tuples = [('1', '2', '3', '4'),
('2', '3', '4', '5'),
('3', '4', '5', '6'),
('4', '5', '6', '7')]
def mod_tuples(list_of_tuples):
for i in range(0, len(list_of_tuples)):
addition = ''
for x in list_of_tuples[i]:
addition = addition + x
list_of_tuples[i] = list_of_tuples[i] + (addition,)
return list_of_tuples
# check:
print mod_tuples(list_of_tuples)
A lot of people is writing tuples are immutables... and that's right! But there is this code:
tuple = ()
for i in range(10):
tuple += (i,)
print(tuple)
And It works! Why? Well.. that's because in the code there is a "=". Every time you use the operator "=" in Python you assign a new value to an object: you create a new variable!
So you are not modifying the old tuple: you are creating a new one, with the same name (tuple), with value the old one plus something more.
OUTPUTS = []
for number in range(len(list_of_tuples))):
tup_ = list_of_tuples[number]
list_ = list(tup_)
item_ = list_[0] + list_[1] + list_[2] + list_[3]
list_.append(item_)
OUTPUTS.append(tuple(list_))
OUTPUTS is what you desire
In case of tuple and list, a very simple way can be (suppose) :
tpl = ( 3, 6, 9)
lst = [ 12, 15, 18]
my_tuple = tpl + tuple(lst)
print(my_tuple)
Related
I was resolving a HackerRank quiz, which consisted in emulating a pagination request.
The function received an array of items, the sort parameter, the sort order, the page number, and the items per page, and returns an array of names corresponding to the given page.
The items had the shape [name: string, relevance: int, price: int].
The sort parameter was 0 for name, 1 for relevance, and 2 for price.
The sort order was 0 for ascending and 1 for descending.
I tried the function in JavaScript and in Python, in case the default sorting function worked different and altered the results. This is my implementation in Python3.
def fetchPaginated(items, sortParameter, sortOrder, pageNum, itemsPerPage):
sortedItems = sorted(items, key=lambda item: item[sortParameter], reverse=False if sortOrder == 0 else True)
paginatedIdx = pageNum * itemsPerPage
slicedItems = sortedItems[paginatedIdx:(paginatedIdx + itemsPerPage)]
return map(lambda item: item[0], slicedItems)
Since HackerRank has hidden test cases, I don't know the inputs for the failing tests. I remember that the size of the array was in the order of the 100s and 1000s, the page number was between 0 and 2, the items per page between 1 and 20. There wasn't a pattern for the sort parameter and order (it wasn't like all the failing tests were for the sort parameter 1 or similar).
Could someone indicate me if my code, or the algorithm behind it has a flaw I don't detect? Maybe a flaw that makes it fail on edge cases?
EDIT:
Link to the HackerRank question. I don't know if it's available to everyone: https://www.hackerrank.com/test/78113p6eaqn
I also had this question on an assessment. The problem is that the input for items is a 2D list of strings, not a list of [name: string, relevance: int, price: int]. Thus when you called sort you were sorting strings of numbers instead of actual numbers, which led to unexpected results. For example:
>>> nums = list(range(1, 6)) + list(range(10, 60, 10))
>>> nums = list(map(str, nums))
>>> nums
['1', '2', '3', '4', '5', '10', '20', '30', '40', '50']
>>> nums.sort()
>>> nums
['1', '10', '2', '20', '3', '30', '4', '40', '5', '50']
A small fix would be to change the key function so that it converts relevance and price (columns 1 and 2) to ints while leaving name (column 0) alone.
sortedItems = sorted(items, key=lambda item: int(item[sortParameter]) if sortParameter else item[sortParameter], reverse=sortOrder)
Given a list which contains both strings and None values, in which some of the strings have embedded newlines, I wish to split the strings with newlines into multiple strings and return a flattened list.
I've written code to do this using a generator function, but the code is rather bulky and I'm wondering if it's possible to do it more concisely using a list comprehension or a function from the itertools module. itertools.chain doesn't seem to be able to decline to iterate any non-iterable elements.
def expand_newlines(lines):
r"""Split strings with newlines into multiple strings.
>>> l = ["1\n2\n3", None, "4\n5\n6"]
>>> list(expand_newlines(l))
['1', '2', '3', None, '4', '5', '6']
"""
for line in lines:
if line is None:
yield line
else:
for l in line.split('\n'):
yield l
You can use yield from.
def expand(lines):
for line in lines:
if isinstance(line,str):
yield from line.split('\n')
elif line is None:
yield line
list(expand(l))
#['1', '2', '3', None, '4', '5', '6']
Here's a single line, but I think #Ch3steR's solution is more readable.
from itertools import chain
list(chain.from_iterable(i.splitlines() if i is not None and '\n' in i else [i]
for i in lines))
You could use itertools.chain if you did the following
import itertools
def expand_newlines(lines):
return itertools.chain.from_iterable(x.split("\n") if x else [None]
for x in lines)
Using more_itertools.collapse to flatten nested lists:
Given
import more_itertools as mit
lst = ["1\n2\n3", None, "7\n8\n9"]
Demo
list(mit.collapse([x.split("\n") if x else x for x in lst ]))
# ['1', '2', '3', None, '7', '8', '9']
more_itertools is a third-party package. Install via > pip install more_itertools.
If you might modify list inplace then you might do:
lst = ["1\n2\n3", None, "4\n5\n6"]
for i in range(len(lst))[::-1]:
if isinstance(lst[i], str):
lst[i:i+1] = lst[i].split("\n")
print(lst) # ['1', '2', '3', None, '4', '5', '6']
this solution utilize fact that you might not only get python's list slices, but also assign to them. It moves from right to left, as otherwise I would need to keep count of additional items, which would make it harder.
Similar to #blueteeth's answer but more concise by way of inverting the logic:
import itertools
chainfi = itertools.chain.from_iterable
def expand_newlines(lines):
r"""Split strings with newlines into multiple strings.
>>> l = ["1\n2\n3", None, "4\n5\n6"]
>>> list(expand_newlines(l))
['1', '2', '3', None, '4', '5', '6']
"""
return chainfi([None] if l is None else l.split('\n') for l in lines)
None is the special case so that's what we should be checking for.
This is concise enough that I wouldn't even bother writing a function for it—I just kept it in the function to confirm it works via doctest.
For example, I have this tuple:
data =(('name/score','game1', 'game2', 'game3', 'game4', 'game5'),('A','1','2','3','4','5'),('B','6','7','8','9','10'),('C','11','12','13','14','15'))
so data is a tuple that contains 4 smaller tuples that contains strings.
Actually data is a 'table' which shows the name A, B, C and D and their respective scores.
How to manipulate data, so that I extra informations I want in data?
For example,
1. how to split data into smaller tuples such as
tuple1 = ('name/score','game1', 'game2', 'game3', 'game4', 'game5')
tuple2 = ('A','1','2','3','4','5')
and so on?
How to remove the 'names', which are A, B and C in each smaller tuple?
I did it by slicing:
newtuple = tuple1[1:]
Just wondering if there is a recursive way or iterative way to do it, cause I dont really get the idea of iteration and recursion.
Is there anyway to define a function which can retrieve the data I want?
for example, I want to know to score of A in game 3, the function should return "3".
data =(('name/score','game1', 'game2', 'game3', 'game4', 'game5'),('A','1','2','3','4','5'),('B','6','7','8','9','10'),('C','11','12','13','14','15'))
The first element of your tuple is a sort of header (like in an excel file the first line).
You want to construct a dictionary of dictionaries where the first level keys are the users (A, B, C, etc.) and the second level of dictionaries have keys like game1, game2, etc. with the value representing the score reached in the given game.
D = dict((t[0], dict(zip(data[0][1:], t[1:]))) for t in data[1:])
dict(zip(data[0][1:], t[1:])) is the part where you create a dictionary from every tuple of data (starting the second tuple) as values using keys from the first tuple of data ("game1", "game2", etc.). We deliberately ignore the very first element of all tuples: "name/score" is ignored, and the user names "A", "B", etc. are also ignored.
Then we "attach" to each dictionary obtained above to a key which is the username: (t[0], dict(zip.... and we obtain a tuple.
Finally from the list of tuples we create a dictionary using the dict builtin function.
The above code will convert your input tuple of tuples to a dictionary of dictionaries like:
{'A': {'game1': '1', 'game2': '2', 'game3': '3', 'game4': '4', 'game5': '5'},
'B': {'game1': '6', 'game2': '7', 'game3': '8', 'game4': '9', 'game5': '10'},
'C': {'game1': '11', 'game2': '12', 'game3': '13', 'game4': '14', 'game5': '15'}}
To get the score of user A in game3 you write:
>>D["A"]["game3"]
3
Since you commented that you don't want to use dictionaries, here is a function that should satisfy your needs:
def get_score(D, user, game):
i = D[0].index(game)
for t in D[1:]:
if t[0] == user:
return t[i]
print get_score(data, "A", "game3")
Probably this simle but somehow I am not able to resolve this. I have a list of list from which I want to strip some characters from every two elements and make another list of list
list = [['A*68:02:01:03', 'A*33:01:01', '1'],
['A*68:02:01:02', 'A*33:01:01', '1'],
['A*68:02:01:01', 'A*33:01:01', '1']]
required output = [['A*68:02', 'A*33:01', '1'],
['A*68:02', 'A*33:01', '1'],
['A*68:02', 'A*33:01', '1']]
Finally I want to print only unique elements. Like in the above case all three elements are same so the output should be:
output = ['A*68:02', 'A*33:01', '1']
Thanks for the help
>>> lst = [['A*68:02:01:03', 'A*33:01:01', '1'], ['A*68:02:01:02', 'A*33:01:01', '1'], ['A*68:02:01:01', 'A*33:01:01', '1']]
>>> newLst = [tuple(':'.join(data.split(':', 2)[:2]) for data in sublist) for sublist in lst]
>>> set(newLst)
{('A*68:02', 'A*33:01', '1')}
The interesting bit is the ':'.join(data.split(':', 2)[:2]. The will split data by colons, take only the first two parts and join them again. That way, we strip everything off after the second colon (including that).
The rest is just a list comprehension to go through the nested list. We also need to convert the inner list to a tuple, so they are hashable when we call set() on it. Doing that will get rid of all duplicates.
I think you want:
lst = [['A*68:02:01:03', 'A*33:01:01', '1'],
['A*68:02:01:02', 'A*33:01:01', '1'],
['A*68:02:01:01', 'A*33:01:01', '1']]
output = []
for item in lst:
processed = [":".join(s.split(":")[:2]) for s in item]
if processed not in output:
output.append(processed)
Note: don't call your own variables things like list.
You could do something like this:
def prefix(string):
"""
Returns the prefix of a string, including all characters
up until the second colon.
"""
return ":".join(string.split(":", 2)[:2])
def unique(iterable):
"""
Returns the unique elements in iterable, maintaining the
elements' relative order.
"""
result = []
seen = set()
for el in iterable:
if el not in seen:
seen.add(el)
result.append(el)
return result
L = [
['A*68:02:01:03', 'A*33:01:01', '1'],
['A*68:02:01:02', 'A*33:01:01', '1'],
['A*68:02:01:01', 'A*33:01:01', '1'],
]
prefixes = [(prefix(el[0]), prefix(el[1]), el[2]) for el in L]
# The built-in class set accepts an iterable and returns a set,
# an object with all duplicate elements removed. Since sets are
# unordered, converting the set back to a list will likely
# produce a list in which the original elements have lost their
# relative order.
# If this is a problem you can use the unique function from above.
uniq = list(set(prefixes))
# If you really need a list of lists, instead of a list of tuples.
uniq = [list(el) for el in uniq]
I have renamed your input list to L, because naming it list shadows the built-in function list.
I very often run into the need to split a sequence into the two subsequences of elements that satisfy and don't satisfy a given predicate (preserving the original relative ordering).
This hypothetical "splitter" function would look something like this in action:
>>> data = map(str, range(14))
>>> pred = lambda i: int(i) % 3 == 2
>>> splitter(data, pred)
[('2', '5', '8', '11'), ('0', '1', '3', '4', '6', '7', '9', '10', '12', '13')]
My question is:
does Python already have a standard/built-in way to do this?
This functionality is certainly not difficult to code (see Addendum below), but for a number of reasons, I'd prefer to use a standard/built-in method than a self-rolled one.
Thanks!
Addendum:
The best standard function I've found so far for handling this task in Python is itertools.groupby. To use it for this particular task however, it is necessary to call the predicate function twice for each list member, which I find annoyingly silly:
>>> import itertools as it
>>> [tuple(v[1]) for v in it.groupby(sorted(data, key=pred), key=pred)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]
(The last output above differs from the desired one shown earlier in that the subsequence of elements that satisfy the predicate comes last rather than first, but this is very minor, and very easy to fix if needed.)
One can avoid the redundant calls to the predicate (by doing, basically, an "inline memoization"), but my best stab at this gets pretty elaborate, a far cry from the simplicity of splitter(data, pred):
>>> first = lambda t: t[0]
>>> [zip(*i[1])[1] for i in it.groupby(sorted(((pred(x), x) for x in data),
... key=first), key=first)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]
BTW, if you don't care about preserving the original ordering, sorted's default sort order gets the job done (so the key parameter may be omitted from the sorted call):
>>> [zip(*i[1])[1] for i in it.groupby(sorted(((pred(x), x) for x in data)),
... key=first)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]
I know you said you didn't want to write your own function, but I can't imagine why. Your solutions involve writing your own code, you just aren't modularizing them into functions.
This does exactly what you want, is understandable, and only evaluates the predicate once per element:
def splitter(data, pred):
yes, no = [], []
for d in data:
if pred(d):
yes.append(d)
else:
no.append(d)
return [yes, no]
If you want it to be more compact (for some reason):
def splitter(data, pred):
yes, no = [], []
for d in data:
(yes if pred(d) else no).append(d)
return [yes, no]
Partitioning is one of those itertools recipes that does just that. It uses tee() to make sure it's iterating the collection in one pass despite the multiple iterators, the builtin filter() function to grab items that satisfies the predicate as well as filterfalse() to get the opposite effect of the filter. This is as close as you're going to get at a standard/builtin method.
def partition(pred, iterable):
'Use a predicate to partition entries into false entries and true entries'
# partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
t1, t2 = tee(iterable)
return filterfalse(pred, t1), filter(pred, t2)
In more_itertools there is a function called partition, which does exactly what topicstarter asked for.
from more_itertools import partition
numbers = [1, 2, 3, 4, 5, 6, 7]
predicate = lambda x: x % 2 == 0
predicate_false, predicate_true = partition(predicate, numbers)
print(list(predicate_false), list(predicate_true))
The result is [1, 3, 5, 7] [2, 4, 6].
If you don't care about efficiency, I think groupby (or any "putting data into n bins" functions) has some nice correspondence,
by_bins_iter = itertools.groupby(sorted(data, key=pred), key=pred)
by_bins = dict((k, tuple(v)) for k, v in by_bins_iter)
You can then get to your solution by,
return by_bins.get(True, ()), by_bins.get(False, ())
A slight variation of one of the OP's implementations and another commenter's implementation above using groupby:
groups = defaultdict(list, { k : list(ks) for k, ks in groupby(items, f) })
groups[True] == the matching items, or [] if none returned True
groups[False] == the non-matching items, or [] if none returned False
Sadly, as you point out, groupby requires that the items be sorted by the predicate first, so if that's not guaranteed, you need this:
groups = defaultdict(list, { k : list(ks) for k, ks in groupby(sorted(items, key=f), f) })
Quite a mouthful, but it is a single expression that partitions a list by a predicate using only built-in functions.
I don't think you can just use sorted without the key parameter, because groupby creates a new group when it hits a new value from the key function. So sorted will only work if the items sort naturally by the predicate provided.
As a slightly more general solution to partitioning, consider grouping. Consider the following function, inspired by clojure's group-by function.
You give it a collection of items to group, and a function that will be used to group them. Here's the code:
def group_by(seq, f):
groupings = {}
for item in seq:
res = f(item)
if res in groupings:
groupings[res].append(item)
else:
groupings[res] = [item]
return groupings
For the OP's original case:
y = group_by(range(14), lambda i: int(i) % 3 == 2)
{False: [0, 1, 3, 4, 6, 7, 9, 10, 12, 13], True: [2, 5, 8, 11]}
A more general case of grouping elements in a sequence by string length:
x = group_by(["x","xx","yy","zzz","z","7654321"], len)
{1: ['x', 'z'], 2: ['xx', 'yy'], 3: ['zzz'], 7: ['7654321']}
This can be extended to many cases, and is a core functionality of functional languages. It works great with the dynamically typed python, as the keys in the resulting map can be any type. Enjoy!
Reducing the iterable into the 2 partitions using functools.reduce you can get rid of the key function:
import functools
functools.reduce(
lambda tf, x: (tf[0], [*tf[1], x]) if pred(x) else ([*tf[0], x], tf[1]),
data,
([], []),
)
>>> (['0', '1', '3', '4', '6', '7', '9', '10', '12', '13'], ['2', '5', '8', '11'])