5

Given a list of hex integers, I want to end up with one hex integer where the integers are treated as most-significant first and least-significant last.

For example, given... [0x1A, 0xF3, 0x74, 0xA3]

...I want to end up with 0x1AF374A3

In another programming language that I'm familiar with this is a fairly simple operation called "join", but apparently "join" means something else in Python.

I know I can iterate through the list multiplying each subsequent element by 2^something and adding them. I also know I can convert the elements to strings, concatenate them and then convert back to integer. But both those approaches seem clunky. Seems like there should be a simpler / more elegant way. Is there?

Thanks in advance for any help!

2
  • you can do str(max(list)) (Will get most significant). Then join the rest as strings replacing 0x for nothing Commented Aug 24, 2018 at 11:34
  • What if the input values can exceed 0xff? Do you care? Commented Apr 4, 2023 at 5:50

7 Answers 7

5

I suggest you to switch the values to bytes type in order to concatenate them, them switch back to int type, as follows:

myList = [0x1A, 0xF3, 0x74, 0xA3]
# Conversion to 'bytes'
a = bytes()
for b in myList:
    a += b.to_bytes(1,'big')
# Conversion back to 'int'
a = int.from_bytes(a, 'big')
print(hex(a))  # 0x1af374a3

And you can replace the classic for loop with a generator comprehension passed as parameter to join() method, in order to concatenate the bytes items. It is still readable and a bit shorter, as follows:

myList = [0x1A, 0xF3, 0x74, 0xA3]
a = b''.join(b.to_bytes(1,'big') for b in myList) # Conversion to 'bytes'
a = int.from_bytes(a, 'big')                      # Conversion back to 'int'
print(hex(a))  # 0x1af374a3

Note that if an integer in the input list exceeds 255, then you logically get the error OverflowError: int too big to convert due to b.to_bytes(1,'big'). Of course it can be improved by managing the exception if this case can happen.

I finally may also suggest you the solution using the multiplication by powers of 256, just to show you that it can be achieved in only one-line:

myList = [0x1A, 0xF3, 0x74, 0xA3]
a = sum(nb*(256**i) for i,nb in enumerate(reversed(myList)))
print(hex(a))  # 0x1af374a3
Sign up to request clarification or add additional context in comments.

5 Comments

+1 for the multiplication. I was just writing it when you stole my answer. :D Just out of curiosity. What would happen in your bytes() method if an integer in list exceeds the maximal size? :D
Well if an integer in the list exceeds 255, then you logically get the error OverflowError: int too big to convert because I consider it as an unexpected input value. Of course it can be improved by managing the exception if this case can happen.
Exactly so. I only thought you should have considered it and mention the fact in the answer.
I completed my answer.
b''.join(b.to_bytes(1,'big') for b in myList) is far too complex. It does the same thing as... wait for it... bytes(myList).
1
l = [0x1A, 0xF3, 0x74, 0xA3]

merged = ''.join([str(hex(el))[2:] for el in l])

If you need the hex you can get it as:

hex(int(merged, 16)) --> 0x1af374a3

Comments

1

There is nothing extremely inefficient nor ugly in the following code, although it may seem so at a first glance. If you need even more speed you will have to use bitwise operators to jam all ints together.

l = [0x1A, 0xF3, 0x74, 0xA3]
i = int("".join(hex(x)[2:] for x in l), 16)
# And to get the hex representation as requested:
print(hex(i).upper())

There is another way to exploit poor strings in this by using the string formatting like this:

i = int(len(l)*"%x" % tuple(l), 16)
print(hex(i))

Comments

0
from functools import reduce
reduce(lambda x, y: x<<8 | y, [0x1A, 0xF3, 0x74, 0xA3])
# 452162723
'0x{:X}'.format(452162723)
# '0x1AF374A3'

The reverse:

>>> [0x1AF374A3 >> i & 0xff for i in reversed(range(0, 32, 8))]
[26, 243, 116, 163]
>>> [hex(0x1AF374A3 >> i & 0xff) for i in reversed(range(0, 32, 8))]
['0x1a', '0xf3', '0x74', '0xa3']

4 Comments

Write some description even though code is descriptive
Thanks to all for the comments. I don't know how this solution compares performance-wise with the others, but it seemed the most elegant, and it seems like "reduce" from "functools" is pretty much specifically created for this kind of requirement. So I'll use it.One small change I made: instead of '0x{:X}'.format(a) I used hex(a). Seemed to give me the same thing. Thanks a lot!!!
Thanks again, duyue! This works great! Now is there an elegant way to do the reverse? Meaning I start with 0x1AF374A3 and I get [0x1A, 0xF3, 0x74, 0xA3]? I looked in functools for the opposite of "reduce" (e.g. "augment", "expand") but didn't find anything that jumped out at me.
@AkashSharma You're welcome. The reverse can be done by [0x1AF374A3 >> i & 0xff for i in reversed(range(0, 32, 8))]
0
mylist =[0x1A, 0xF3, 0x74, 0xA3]

most_significant = str(hex(max(mylist)))
print(most_significant + ''.join([str(el).replace('0x', '') for el in mylist if el != max(mylist)]))

0xf326116163

1 Comment

If I am not mistaken, your answer does not match the question asked. The question was to combine the integers in the order they occur in the list, not with the biggest number in front.
0

You can convert each to decimal then back to hex but remove the 0x, the join them together and then add 0x at the beginning. format() is good at doing such conversions.

mylist = [0x1A, 0xF3, 0x74, 0xA3]
result = '0x' + ''.join([format(int(n), 'x')for n in [format(i , 'd') for i in mylist]])

Output:

'0x1af374a3'

Comments

0

I think there is a clean and "pythonic" way to solve this, using the Python bytearray type:

foolist = [0x1A, 0xF3, 0x74, 0xA3]
# convert to bytearray and then to a single integer in one line:
x = int.from_bytes(bytearray(foolist), "big")
print(hex(x))
# '0x1af374a3'

In case the most significant byte in the list is at the end of the list, use the parameter "little" instead of "big" in the int.from_bytes function.

In case your list contains an integer bigger than 255, ValueError exception will be raised:

>>> foolist1 = [0xFF, 255]                                                                                 

>>> bytearray(foolist1)                                                                                    
bytearray(b'\xff\xff')


>>> foolist2 = [0xFF, 256]                                                                                 

>>> bytearray(foolist2)                                                                                    
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: byte must be in range(0, 256)

byte must be in range(0, 256)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.