Python Count Unique Elements for Multidimensional List - python

I've written a function to count unique elements in a 2d list in Python, and I'm wondering how to make it more general for lists of any number of dimensions. Answers I've seen so far about finding the dimension by testing if the first instance is a list don't seem robust. Please note I want to avoid using NumPy.
Any suggestions please?
def count_unique_elements_2d_list(list_2d):
flat_list = []
for sublist in list_2d:
for item in sublist:
flat_list.append(item)
unique_recipes = set(flat_list)
return len(unique_recipes)

This will return on the unique elements, which you can then call len on.
def get_unique(some_array, seen=None):
if seen is None:
seen = set()
for i in some_array:
if isinstance(i, list):
seen.union(get_unique(i, seen))
else:
seen.add(i)
return seen
What is happening?
This is a recursive problem. If the level is not a list, then assume it's an item and add it to the set of seen items. if it is a list, then run the function on it again.

You can avoid the extra space you have used by running two for loops and adding unique elements to the set only.
def count_unique_elements_2d_list(list_2d):
unique=set()
for sublist in list_2d:
for item in sublist:
unique.add(item)
return len(unique)
If the element is already present it would not be added to the set. And you will get the unique count of elements.

Here is my quick hack. It's a little unclean with the except but it's short and relatively efficient.
def get_dimensions(lut):
dim = 0
test = lut
try:
while test[0]:
dim = dim + 1
test = test[0]
except TypeError:
return dim
def to_onedimensional(lut, dim, output, counter):
if(counter == dim - 1):
for x in lut:
output.append(x)
else:
for x in lut:
to_onedimensional(x, dim, output, counter + 1)
This is how to use it:
lut = [[[3],[4]], [[2],[3]]]
print(get_dimensions(lut))
output = []
to_onedimensional(lut, get_dimensions(lut), output, 0)
res = set(output)
print(len(res))
Output:
3
3

Related

Display only 1 element when it's a repetition

I would like print a result without duplicate with my multiplication
Here an example :
5*3*2=30
2*3*5=30
5*2*3=30
3*2*5=30
.....
All these element are from my list that I browse and you can see it's always =30
So I would like display only the first element (5*3*2) and not others because they are the same.
To be more accurate, here an example what I have :
list = ['5*3*2','5*2*3','2*3*5','2*5*3']
for i in list:
if eval(i) == eval(i)+1 ??? (I dont know how to say the next element)
print(eval(i))
Thanks for reading
Something like this with not in will help you.
#python 3.5.2
list = ['5*3*2','5*2*3','6*9','2*3*5','2*5*3','8*3','9*6']
elm = []
for i in list:
elm_value = eval(i)
if elm_value not in elm:
elm.append(elm_value)
print(elm)
DEMO: https://rextester.com/QKV22875
The comparison:
eval(i) == eval(i)+1
Will compare if the the number i is equal to i + 1, which will always return False. I'm sure you mean to use i as an index and simply wanted to compare if the current element is equal to the next element in the list. However, doing this doesn't really keep track of duplicates, since you have to consider everything else in the list.
Its also not a good idea to use list as a variable name, since it shadows the builtin function list. Plenty of other suitable names you can use.
One way is to use a set to keep track of what items you have seen, and only print items that you have seen for the first time:
lst = ["5*3*2","5*2*3","2*3*5","2*5*3"]
seen = set()
for exp in lst:
calc = eval(exp)
if calc not in seen:
print(calc)
seen.add(calc)
If you are always dealing with simple multiplying expressions with the * operator(no brackets), you could also use functools.reduce and operator.mul instead to multiply the numbers instead of eval here. This will first split the numbers by *, map each number string to an integer, then multiply every element with each other.
from operator import mul
from functools import reduce
lst = ["5*3*2","5*2*3","2*3*5","2*5*3"]
seen = set()
for exp in lst:
numbers = map(int, exp.split("*"))
calc = reduce(mul, numbers)
if calc not in seen:
print(calc)
seen.add(calc)
Output:
30
With the following list:
l = ['5*3*2','5*2*3','2*3*5','2*5*3', '2*2']
(Note that list is already something in python so I wouldn't recommend using that as a variable name)
I would first create a list of unique values:
unique_vals = set(map(eval, list))
set([4, 30])
Then for each unique values get the first match in l:
[next(x for x in l if eval(x) == i) for i in unique_vals]
I get:
['2*2', '5*3*2']
Is that what you want?

return a new list that interleaves the two lists but with a twist

def back_interleave(first, second):
if first == [] and second == []:
return []
elif first == []:
return second[::-1]
elif second == []:
return first[::-1]
else:
newlist = []
for i in range(len(first)-1, 0,-1):
newlist.append(first[i])
newlist.append(second[i])
for j in range(len(second)-len(first)-1,0,-1):
newlist.append(second[i])
return newlist
can anybody tells me what's wrong with my code towards this question.
I'm not exactly sure what's wrong with your code, but the second and third if-statements appear to use built-in list reversing functionality which the original problem forbids.
What I would do is determine the length of the longer list, then iterate through both lists backwards.
def back_interleave(first, second):
newlist = []
# You want to iterate through the length of the longer list
length = max(len(first), len(second))
for x in range(length):
# start appending elements from the back of the list
index = -1*(x+1)
if x < len(first):
newlist.append(first[index])
if x < len(second):
newlist.append(second[index])
return newlist
The problem in your code is when you use the range function, the stop value is exclusive i.e., the index 0 is becoming exclusive in your case. And also in the j loop the values at index i are being stored instead of the values at index j.
#CyanideTesla has given the code that works pretty well for your problem

find all element positions within a list of lists

Given that l = [[1,2,3], [3,4,5],[5,6]], is there a method to return a list that consists of all positions of 3 within l; i.e. return [2,3]. A sample code I wrote is:
def pos_in_lists(seq, elem):
while i < len(seq):
elem_pos = seq[i].index(elem)
return [elem_pos]
When I run this, it only returns 2, which is not the result I want. What is my mistake? Also, is there a more simple way to solve my problem?
Your code only returns a list with one element (return [elem_pos]). You need to have a list variable (result) outside the loop to keep track of the result of previous lists by appending to that list.
def pos_in_lists(seq, elem):
result = []
i = 0
while i < len(seq):
if elem in seq[i]:
elem_pos = seq[i].index(elem)
result.append(elem_pos)
i += 1
return result
Another simpler alternative is to use list comprehension
def pos_in_lists(seq, elem):
return [l.index(elem) for l in seq if elem in l]
You need to increment "i" each loop
"return" forces the loop to exit. That should be outside the loop at the end of the code.
According to my understanding of the problem, Given list l = [[1,2,3], [3,4,5],[5,6]] and if we have to find 3, the output list should be [2,0].
Following mistakes are there in the code provided:
The code is not working as i is not defined in your method before using it.
We need to store positions of all 3 value present in the list, thus we need to have a list in which we store all the positions of 3 that we can find in the list. You have returned the result as soon as you have found one value. Thus, you are getting only 2.
seq[i].index(elem) will throw value error if 3 is not in the list.
Solution
def pos_in_lists(seq, elem):
res = []
for innerList in seq:
if elem in innerList:
res.append(innerList.index(elem))
return res
pos_in_lists(l,3)
Result would be [2,0]
we can use list comprehension as well:
def pos_in_lists(seq, elem):
return [innerList.index(elem) for innerList in seq if elem in innerList]

Why my 2nd method is slower than my 1st method?

I was doing leetcode problem No. 387. First Unique Character in a String. Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1.
Examples:
s = "leetcode"
return 0.
s = "loveleetcode",
return 2.
I wrote 2 algorithm:
Method 1
def firstUniqChar(s):
d = {}
L = len(s)
for i in range(L):
if s[i] not in d:
d[s[i]] = [i]
else:
d[s[i]].append(i)
M = L
for k in d:
if len(d[k])==1:
if d[k][0]<M:
M = d[k][0]
if M<L:
return M
else:
return -1
This is very intuitive, i.e., first create a count dictionary by looping over all the char in s (this can also be done using one line in collections.Counter), then do a second loop only checking those keys whose value is a list of length 1. I think as I did 2 loops, it must have some redundant computation. So I wrote the 2nd algorithm, which I think is better than the 1st one but in the leetcode platform, the 2nd one runs much slower than the 1st one and I couldn't figure out why.
Method 2
def firstUniqChar(s):
d = {}
L = len(s)
A = []
for i in range(L):
if s[i] not in d:
d[s[i]] = i
A.append(i)
else:
try:
A.remove(d[s[i]])
except:
pass
if len(A)==0:
return -1
else:
return A[0]
The 2nd one just loop once for all char in s
Your first solution is O(n), but your second solution is O(n^2), as method A.remove is looping over elements of A.
As others have said - using list.remove is quite expensive... Your use of collections.Counter is a good idea.
You need to scan the string once to find uniques. Then probably what's better is to sequentially scan it again and take the index of the first unique - that makes your potential code:
from collections import Counter
s = "loveleetcode"
# Build a set of unique values
unique = {ch for ch, freq in Counter(s).items() if freq == 1}
# re-iterate over the string until we first find a unique value or
# not - default to -1 if none found
first_index = next((ix for ix, ch in enumerate(s) if ch in unique), -1)
# 2

Python - dividing a list and maintaining the original index (Recursion)

i have homework in which i must use recursion to find all occurances of a number/letter/word in a list and return their index in the original list.. I have searched this site for previously answered question but I couldn't find any answer regarding recursion with the option to continue checking the list even after the first occurances has been found..
should look like this pretty much:
>>> find_value( [4,7,5,3,2,5,3,7,8,6,5,6], 5)
[2,5,10]
my code so far goes like this:
def find_all(x,y):
if len(x) == 1 and x[0] == y:
return [i for i, y in enumerate(x)]
return find_all(x[1:],y)
Though it only minimize the list and gives me the same [0] as the index.. which is true, for the divided list.. this way I will never get the original index..
Thanks
- if this already exist, I am sorry for i have searched and couldn't find.
Here is a simple non-recursive solution:
def find_value(l, lookfor):
return [i for i, v in enumerate(l) if v == lookfor]
As a piece of advice for your homework -- just pass the progress through the list as an optional third argument to find_all:
def find_value(list, lookfor, position=0)
... and add one to position each time you recurse.
The point of assigning a homework is usually so that you can explore the problem and learn from it. In this case, it is recursion which is usually hard for beginners.
The point of recursion is to construct an answer for a larger problem from a solution of a smaller ones. So it is best to start off with the smallest one possible:
def find_all(haystack, needle):
if not haystack:
# no occurrences can happen
return []
If the list is not empty, we can check if the first element is what we are looking for:
if haystack[0] == needle:
occurrences = [0] # the index of the first element is always 0
else:
occurrences = []
We will also need the solution to the smaller problem:
recursive_occurences = find_all(haystack[1:], needle)
Now the problem you have noticed is that the indices that are returned are always 0. That's because they are indices in the smaller list. If an item has index 0 in a smaller list, it means its index in the largest list is actually 1 (this is the main part your program was missing), thus:
for x in recursive_occurences:
occurrences.append(x+1)
And return the complete answer:
return occurrences
I hope this helps you a bit, so you can do your next homework on your own.
Here are several solution:
in one go, ugly, but working:
def find_value(lst, elt):
return [x + 1
for x in ([] if not lst else
(([-1] if lst[0] == elt else []) +
find_value(lst[1:], elt)))]
prettier, but with hidden index param:
def find_value(lst, elt, idx=0):
return [] if not lst else \
(([idx] if lst[0] == elt else []) +
find_value(lst[1:], elt, idx + 1))
pretty?, long with inner recursive function... more maintainable ?
def find_value(lst, elt):
def _rec(lst, elt, idx):
if not lst:
return []
res = [idx] if lst[0] == elt else []
return res + _rec(lst[1:], elt, idx + 1)
return _rec(lst, elt, idx=0)
There is a very simple solution to this problem, even if you are using recursion to solve the assignment:
>>> def find_value(array, value):
*head, tail = array
array = find_value(head, value) if head else []
return array + [len(head)] if tail == value else array
>>> find_value([4, 7, 5, 3, 2, 5, 3, 7, 8, 6, 5, 6], 5)
[2, 5, 10]
>>>

Categories