Why is the output "cbe" rather than "bce" in this python program? - python

getDifference=lambda string1, string2: reduce((lambda character1, character2: character1+character2), (set(string1)-set(string2)))
print getDifference('abcde','adf')
In the first line, I defined a lambda expression that finds the difference between string1 and string2. I assume the output should be "bce", but it is "cbe", why?

A set is an unordered collection of unique elements - so the order of the characters is not kept through the sets operation. Check here for more:
http://docs.python.org/2/tutorial/datastructures.html#sets

Python sets don't preserve ordering. To preserve the order, you can use a list comprehension:
def diff(s1, s2):
return "".join([c for c in s1 if c not in s2])
diff('abcde','adf') # 'bce'
Many set implementations use hashing, which mixed up the order of the elements as a side effect. Ordered set implementations such as treesets do exist, however.

Related

How to get into details of str() and int() conversion [duplicate]

Tackling a few puzzle problems on a quiet Saturday night (wooohoo... not) and am struggling with sort(). The results aren't quite what I expect. The program iterates through every combination from 100 - 999 and checks if the product is a palindome. If it is, append to the list. I need the list sorted :D Here's my program:
list = [] #list of numbers
for x in xrange(100,1000): #loops for first value of combination
for y in xrange(x,1000): #and 2nd value
mult = x*y
reversed = str(mult)[::-1] #reverses the number
if (reversed == str(mult)):
list.append(reversed)
list.sort()
print list[:10]
which nets:
['101101', '10201', '102201', '102201', '105501', '105501', '106601', '108801',
'108801', '110011']
Clearly index 0 is larger then 1. Any idea what's going on? I have a feeling it's got something to do with trailing/leading zeroes, but I had a quick look and I can't see the problem.
Bonus points if you know where the puzzle comes from :P
You are sorting strings, not numbers. '101101' < '10201' because '1' < '2'. Change list.append(reversed) to list.append(int(reversed)) and it will work (or use a different sorting function).
Sort is doing its job. If you intended to store integers in the list, take Lukáš advice. You can also tell sort how to sort, for example by making ints:
list.sort(key=int)
the key parameter takes a function that calculates an item to take the list object's place in all comparisons. An integer will compare numerically as you expect.
(By the way, list is a really bad variable name, as you override the builtin list() type!)
Your list contains strings so it is sorting them alphabetically - try converting the list to integers and then do the sort.
You're sorting strings, not numbers. Strings compare left-to-right.
No need to convert to int. mult already is an int and as you have checked it is a palindrome it will look the same as reversed, so just:
list.append(mult)
You have your numbers stored as strings, so python is sorting them accordingly. So: '101x' comes before '102x' (the same way that 'abcd' will come before 'az').
No, it is sorting properly, just that it is sorting lexographically and you want numeric sorting... so remove the "str()"
The comparator operator is treating your input as strings instead of integers. In string comparsion 2 as the 3rd letter is lexically greater than 1.
reversed = str(mult)[::-1]

Generate wordlist with known characters

I'm looking to write a piece of code in Javascript or Python that generates a wordlist file out of a pre-defined combination of characters.
E.g.
input = abc
output =
ABC
abc
Abc
aBc
abC
AbC
ABc
aBC
I have very basic knowledge of either so all help is appreciated.
Thank you
I'll assume that you're able to import Python packages. Therefore, take a look at itertools.product:
This tool computes the cartesian product of input iterables.
For example, product(A, B) returns the same as ((x,y) for x in A for y in B).
It looks quite like what you're looking for, right? That's every possible combination from two different lists.
Since you're new to Python, I'll assume you don't know what a map is. Nothing too hard to understand:
Returns a list of the results after applying the given function to each item of a given iterable (list, tuple etc.)
That's easy! So the first parameter is the function you want to apply and the second one is your iterable.
The function I applied in the map is as follows:
''.join
This way you set '' as your separator (basically no separator at all) and put together every character with .join.
Why would you want to put together the characters? Well, you'll have a list (a lot of them in fact) and you want a string, so you better put those chars together in each list.
Now here comes the hard part, the iterable inside the map:
itertools.product(*((char.upper(), char.lower()) for char in string)
First of all notice that * is the so-called splat operator in this situation. It splits the sequence into separate arguments for the function call.
Now that you know that, let's dive into the code.
Your (A, B) for itertools.product(A, B) are now (char.upper(), char.lower()). That's both versions of char, upper and lowercase. And what's char? It's an auxiliar variable that will take the value of each and every character in the given string, one at a time.
Therefore for input 'abc' char will take values a, b and c while in the loop, but since you're asking for every possible combination of uppercase and lowercase char you'll get exactly what you asked for.
I hope I made everything clear enough. :)
Let me know if you need any further clarification in the comments. Here's a working function based on my previous explanation:
import itertools
def func():
string = input("Introduce some characters: ")
output = map(''.join, itertools.product(*((char.upper(), char.lower()) for char in string)))
print(list(output))
As an additional note, if you printed output you wouldn't get your desired output, you have to turn the map type into a list for it to be printable.
A simple approach using generators, and no library code. It returns a generator (iterator-like object), but can be converted to a list easily.
def lU(s):
if not s:
yield ''
else:
for sfx in lU(s[1:]):
yield s[0].upper() + sfx
yield s[0].lower() + sfx
print list(lU("abc"))
Note that all the sub-lists of suffixes are not fully expanded, but the number of generator objects (each a constant size) that get generated is proportional to the length of the string.

Trying to match an exact subset in python

I'm trying to see if a set in python contains the elements of another set. I've tried to use set comparison but the problem is I need to be able to recognise only an exact match of elements. For example a subset of [3,3] will match a set of [3,1,2] when I want it to only match to [3,3,1], or any set variant with two threes.
I am iterating through all possible variants of a 3 element set using numbers 0-4, trying to see which ones contain the [3,3] set. Should I be using sets or is it better to use a list? Any ideas on how to do this?
Cheers
sets cannot contain duplicate elements. you can use a list or a dict where the value for the key is the number of times the key occurs in your set.
Something like:
d1 = {3:2, 1:1}
d2 = {3:2}
all(d1.get(k,0)-v>=0 for (k,v) in d2.items())
Assuming that by set you mean list, something like this should work (not tested):
def contains(superset, subset):
for elem in set(superset):
if superset.count(elem) < subset.count(elem):
return False
return True
If you want to have duplicates in your sets (making them really multisets or "bags", rather than proper sets), you should use collections.Counter. It supports set operations (&, +, -) with appropriate multiset semantics.
Testing if one multiset a is a subset of another multiset b can be done with a == a & b:
from collections import Counter
a = Counter([3,3])
b = Counter([3,1,2])
print(a == a & b) # prints False, since a is not a subset of b

Add string to another string

I currently encountered a problem:
I want to handle adding strings to other strings very efficiently, so I looked up many methods and techniques, and I figured the "fastest" method.
But I quite can not understand how it actually works:
def method6():
return ''.join([`num` for num in xrange(loop_count)])
From source (Method 6)
Especially the ([`num` for num in xrange(loop_count)]) confused me totally.
it's a list comprehension, that uses backticks for repr conversion. Don't do this. Backticks are deprecated and removed in py3k and more efficient and pythonic way is not to build intermediate list at all, but to use generator expression:
''.join(str(num) for num in xrange(loop_count)) # use range in py3k
xrange() is a faster (written in C) version of range().
Backtick notation -- num, coerces a variable to a string, and is the same as str(num).
[x for x in y] is called a list comprehension, and is basically an one-liner for loop that returns a list as its result. So all together, your code's semantically equivalent to the following, but faster, because list comprehensions and xrange are faster than for loops and range:
z = []
for i in range(loop_count):
z.append(str(i))
return "".join(z)
That bit in the brackets is a list comprehension, arguably one of the most powerful elements of Python. It produces a list from iteration. You may want to look up its documentation. The use of backticks to convert num to a string is not suggestible - try str(num) or some such instead.
join() is a method of the string class. It takes a list of strings and return a single string consisting of each component string separated by "self" (aka the calling string). The trick here is that join() is being called directly from the string literal '', which is allowed in Python. What this code will to is produce a string consisting of the string form of each element of xrange(loop_count) with no separation.
First of all: while this code is still correct in the 2.x series of Python, it a bit confusing and can be written differently:
def method6a():
return ''.join(str(num) for num in xrange(loop_count))
In Python 2.x, the backticks can be used instead of the repr function. The expression within the square brackets [] is a list comprehension. In case you are new to list comprehensions: they work like a combination of a loop and a list append-statement, only that you don't have to invent a name for a variable:
Those two are equivalent:
a = [repr(num) for num in xrange(loop_count)]
# <=>
a = []
for num in xrange(loop_count):
a.append(repr(num))
As a result, the list comprehension will contain a list of all numbers from 0 to loop_count (exclusively).
Finally, string.join(iterable) will use the contents of string concatenate all of the strings in iterable, using string as the seperator between each element. If you use the empty string as the seperator, then all elements are concatenated without anything between them - this is exactly what you wanted: a concatenation of all of the numbers from 0 to loop_count.
As for my modifications:
I used str instead of repr because the result is the same for all ints and it is easier to read.
I am using a generator expression instead of a list comprehension because the list built by the list comprehension is unnecessary and gets garbage collected anyway. Generator expressions are iterable, but they don't need to store all elements of the list. Of course, if you already have a list of strings, then simply pass the list to the join.
Generally, the ''.join(iterable) idiom is well understood by most Python programmers to mean "string concatenation of any list of strings", so understandability shouldn't be an issue.

Python .sort() not working as expected

Tackling a few puzzle problems on a quiet Saturday night (wooohoo... not) and am struggling with sort(). The results aren't quite what I expect. The program iterates through every combination from 100 - 999 and checks if the product is a palindome. If it is, append to the list. I need the list sorted :D Here's my program:
list = [] #list of numbers
for x in xrange(100,1000): #loops for first value of combination
for y in xrange(x,1000): #and 2nd value
mult = x*y
reversed = str(mult)[::-1] #reverses the number
if (reversed == str(mult)):
list.append(reversed)
list.sort()
print list[:10]
which nets:
['101101', '10201', '102201', '102201', '105501', '105501', '106601', '108801',
'108801', '110011']
Clearly index 0 is larger then 1. Any idea what's going on? I have a feeling it's got something to do with trailing/leading zeroes, but I had a quick look and I can't see the problem.
Bonus points if you know where the puzzle comes from :P
You are sorting strings, not numbers. '101101' < '10201' because '1' < '2'. Change list.append(reversed) to list.append(int(reversed)) and it will work (or use a different sorting function).
Sort is doing its job. If you intended to store integers in the list, take Lukáš advice. You can also tell sort how to sort, for example by making ints:
list.sort(key=int)
the key parameter takes a function that calculates an item to take the list object's place in all comparisons. An integer will compare numerically as you expect.
(By the way, list is a really bad variable name, as you override the builtin list() type!)
Your list contains strings so it is sorting them alphabetically - try converting the list to integers and then do the sort.
You're sorting strings, not numbers. Strings compare left-to-right.
No need to convert to int. mult already is an int and as you have checked it is a palindrome it will look the same as reversed, so just:
list.append(mult)
You have your numbers stored as strings, so python is sorting them accordingly. So: '101x' comes before '102x' (the same way that 'abcd' will come before 'az').
No, it is sorting properly, just that it is sorting lexographically and you want numeric sorting... so remove the "str()"
The comparator operator is treating your input as strings instead of integers. In string comparsion 2 as the 3rd letter is lexically greater than 1.
reversed = str(mult)[::-1]

Categories