Posted by: xSicKxBot - 10-25-2022, 09:13 AM - Forum: Python
- No Replies
Solidity Bytes and String Arrays, Concat, Allocating Memory, and Array Literals
Rate this post
With this article, we’ll discover a new and fascinating world of bytes and strings, as well as ways to manipulate them, allocate memory arrays, and use array literals.
It’s part of our long-standing tradition to make this (and other) articles a faithful companion, or a supplement to the official Solidity documentation, starting with these docs for this article’s topics.
Types bytes and string
Besides the arrays we’ve already discussed, there are also some unique arrays, such as bytes and string arrays.
We have to note that the bytes type is very similar to bytes1[], however, the difference is that a bytes array is tightly packed in memory areas calldata and memory.
Furthermore, string is equal to bytes, but does not have a length property or support for index access.
Solidity doesn’t have string manipulation functions compared to other commonly used programming languages, but this can be worked around by including third-party string libraries.
With vanilla Solidity, we can concatenate two strings, e.g. string.concat(s1, s2), and compare two strings by using their keccak-256 hash, e.g.
Regarding the preferred use (we could consider this a design pattern), the bytes type is better than bytes1[], because bytes1[] is more expensive due to padding additional 31 bytes between the elements when used in memory.
The padding is absent in storage because of the tight packing used (docs).
Note: A rule of thumb says that bytes should be used for arbitrary-length raw byte data and string for arbitrary-length string data in UTF-8.
Note: If our data can be stored in a variable containing a number of bytes up to 32, it is better to use one of the value types bytes1 ... bytes32, due to their low cost.
To access a byte representation of a string s, we could use the following construct: bytes(s)[7] = 'x'; with regard to the string length, bytes(s).length, e.g.
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; /** * @title String modification * @dev Demonstrates how to modify a string represented as bytes. */
contract StringModification { string public s = "Some string"; function modifyString() public { bytes(s)[7]='Q'; }
}
Note: By using this approach, we’re accessing bytes of the UTF-8 representation, not the individual characters.
Functions bytes.concat() and string.concat()
Concatenation is a synonym for joining or gluing together.
The function string.concat() enables us to concatenate any number of string values.
The result of using the string.concat() function is a single-string memory array containing the concatenated strings without any added spacing or padding.
If we’d like to use function parameters of other types that are not implicitly convertible to the string type, we first have to convert them to the string type.
Byte Concatenation
In the same manner, the bytes.concat() function enables us to concatenate any number of bytes or bytes1 ... bytes32 values.
The function result is a single bytes memory array containing the arguments without padding.
If we’d like to use string parameters or other types not implicitly convertible to bytes type, we first convert them to the bytes type.
Example
Let’s use an example to show how a function performs both string and bytes concatenation:
By calling bytes.concat(...) and string.concat(...) without arguments, a result is an empty array.
Allocating Memory Arrays
We can dynamically resize the storage arrays by adding elements via the .push() member function.
In contrast, memory arrays cannot be dynamically resized and the .push() member function is not available.
However, by using the alternative approach, we can create dynamic-length memory arrays by using the new operator. Just before using the new operator, we have to calculate the required size in advance or create a new, empty array and populate it by copying all elements.
Note: Following the same rule of default values, the elements of freshly allocated arrays are initialized with their default values (docs).
Here we have an example showing arrays a and b, initialized by either a constant size or a parameter-given size.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0; contract C { function f(uint len) public pure { uint[] memory a = new uint[](7); bytes memory b = new bytes(len); assert(a.length == 7); assert(b.length == len); a[6] = 8; }
}
Array Literals
Array literal is represented by a comma-separated list of any number of expressions, which are listed in square brackets, e.g. [1, a, f(3)].
The array literal type is determined in the following way:
The array literal is a statically-sized memory array, and its length is the number of expressions listed in the brackets;
The base type of the array is determined by the type of the first expression T in the list that satisfies the condition: all other expressions must be implicitly convertible to T. If it’s not possible to find such an expression, a type error is thrown;
Besides the convertibility condition (point 2.), one of the expressions must be of the T type.
The following example will clarify what the points above mean; the type of an array literal [1, 2, 3] is uint8[3] memory, because each of the expressions is of type uint8.
If we want to change the result to type uint[3] memory, we have to convert the first element to uint.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0; contract C { function f() public pure { g([uint(1), 2, 3]); } function g(uint[3] memory) public pure { // ... }
}
In contrast, the array literal [1, -2] is invalid because it doesn’t comply with point 2., stating that the first expression’s type is a target type T for implicit conversion of other expressions.
Since our first expression is of type uint8, and the second expression is of type int8 (including the negative numbers), the second expression cannot be implicitly converted to uint8.
To avoid a type error, we can declare our array literal as [int8(1), -1], forcing the first expression to be of compatible type int8.
In a more specific case of using, e.g. two-dimensional array literals, we’d step on a problem of fixed-size memory arrays that cannot be converted into each other, regardless of the compatibility of base types.
We can get around this problem by explicitly specifying a common base:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0; contract C { function f() public pure returns (uint24[2][4] memory) { uint24[2][4] memory x = [[uint24(0x1), 1], [0xffffff, 2], [uint24(0xff), 3], [uint24(0xffff), 4]]; // The following does not work, because some of the inner arrays are not of the right type. // uint[2][4] memory x = [[0x1, 1], [0xffffff, 2], [0xff, 3], [0xffff, 4]]; return x; }
}
We cannot assign fixed-size memory arrays to dynamically-sized memory arrays, as shown by the example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0; // This will not compile.
contract C { function f() public { // The next line creates a type error because uint[3] memory // cannot be converted to uint[] memory. uint[] memory x = [uint(1), 3, 4]; }
}
To initialize dynamically-sized arrays, we’d have to resort to assigning the elements individually, as in the example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0; contract C { function f() public pure { uint[] memory x = new uint[](3); x[0] = 1; x[1] = 3; x[2] = 4; }
}
Conclusion
In this article, we learned even more about reference types, in particular, bytes and string arrays and concatenation, memory array allocation, and array literals.
First, we explained the uniqueness of the arrays based on bytes and string types, and also touched on some of the similarities with the akin types.
Second, we’ve peeked into how to do string concatenation, comparison, and bytes concatenation.
Third, we discovered the specifics of allocating memory arrays and got introduced to the new operator.
Fourth, we got to know array literals with rules for determining the array literal base type. We also became aware of the invalid array literals and what can be done to make them valid.
What’s Next?
This tutorial is part of our extended Solidity documentation with videos and more accessible examples and explanations. You can navigate the series here (all links open in a new tab):
Death is only the beginning. Crawl from your grave and gear up to fight your way through the ranks of a mysterious and twisted cult. You, your guns, and your dynamite will have to shoot, slide, blast, duck, dodge, and maybe throw a gib or two to survive in this old-school-inspired shooter.
How to Convert Pandas DataFrame/Series to NumPy Array?
5/5 – (1 vote)
Programming Challenge: Given a Pandas DataFrame or a Pandas Series object. How to convert them to a NumPy array?
In this short tutorial, you’ll learn (1) how to convert a 1D pandas Series to a NumPy array, and (2) how to convert a 2D pandas DataFrame to an array. Let’s get started with the first!
Attention: There is also the .values() method, but that is being deprecated now – when you look at the Pandas documentation, there is a warning “We recommend using DataFrame.to_numpy instead”.
With this method, only the values in the DataFrame or Series will return. The index labels will be removed.
Here’s how that’ll work:
print(df.values)
# [22 21 20 14]
This was a 1-dimensional array or a Series. Let’s move on to the 2D case next.
Convert DataFrame to NumPy Array
Question: Let’s try with a two-dimensional DataFrame — how to convert it to a NumPy array?
First, let’s print the dimension of the previous Series to confirm that it was, indeed, a 1D data structure:
Now, let’s dive into the conversion of this DataFrame to a NumPy array by using the DataFrame.to_numpy() method.
# Convert this DataFrame to a NumPy array
print(df2.to_numpy())
The output shows a NumPy array from the 2D DataFrame — great!
[[ 2 9 6 2] [14 2 1 0] [ 2 7 8 7] [ 4 3 5 5]]
You can see that all indexing metadata has been stripped away from the resulting NumPy array!
Convert Specific Columns from DataFrame to NumPy Array
You can also convert specific columns of a Pandas DataFrame by accessing the columns using pandas indexing and calling the .to_numpy() method on the resulting view object.
Here’s an example:
print(df2[['Djokovic', 'Federer']].to_numpy())
The output:
[[9 6] [2 1] [7 8] [3 5]]
Summary
You can convert a Pandas DataFrame or a Pandas Series object to a NumPy array by means of the df.to_numpy() method. The indexing metadata will be removed.
You can also convert specific columns of a Pandas DataFrame by accessing the columns using pandas indexing and calling the .to_numpy() method on the resulting view object.
FREE SuperTotalCarnage, Uncharted, Anno & Tropico Deals
SuperTotalCarnage FREEbie
[supertotalcarnage.indiegala.com] Welcome to SuperTotalCarnage, our new a fast-paced time survival game where you face endless enemies! This new freebie is up for an indefinite period of time, in anticipation of the Steam early access release coming soon.
How Long Is Call Of Duty: Modern Warfare 2's Campaign?
Call of Duty: Modern Warfare 2's campaign is currently available in early access, featuring the familiar face of Captain Price and a fully-assembled Task Force 141 on a brand-new blockbuster mission to stop stolen missiles from being used in a revenge plot against the United States. If you're looking to set aside some time to play Modern Warfare 2's campaign, here's everything you need to know.
How long does it take to beat CoD: MW2's campaign?
Call of Duty: Modern Warfare 2's campaign includes 17 story missions, and you can expect to carve out around five to six hours of game time to complete the campaign on the Regular "normal" difficulty setting. This doesn't steer too far away from Modern Warfare 2019, which featured a campaign length of about 5 hours.
For my first experience, playing Modern Warfare 2 on Regular difficulty did take me a bit beyond the six-hour mark, and that's longer than my usual campaign efforts. This year's story features some missions that are less linear than we've seen in previous campaigns, as you sometimes have more open-world environments to explore and choose how you want to approach your objectives.
The Case of the Golden Idol is a wryly amusing detective game set in the 18th century, brought to life with striking hand-drawn artwork. The game tests your ability to piece together clues and reconstruct the events leading up to some mysterious deaths.
Investigate a series of mysterious deaths. Explore hand-drawn locations set in the 18th century, where some mysterious deaths have taken place.
Reconstruct the events with a unique drag-and-drop mechanic. Gather verbal evidence and visual clues, then apply your deduction skills to figure out what actually happened and how each victim died.
Explore a greater mystery that spans centuries. Start revealing a larger overarching story that connects all the individual tales.
[www.indiegala.com] A one of a kind selection, games made with heart and mind brought to you by Whale Rock Games for the passionate gamers: Time Lock VR 2, The Divine Invasion, NeverSynth, SPACE ACCIDENT, Dofamine & Euphoria: Supreme Mechanics
The land of Benerio was once a shining kingdom by the sea. Then an iron-fisted conqueror, Ludivictus, set the world on fire and redrew the maps. Now Benerio is one downtrodden province in a crumbling Empire where the only true master is corruption.
Enter Eden, a charming mercenary captain forced to become a rebel commander when his village is thrust headfirst into war. Now Eden must rally his allies and stand tall against an imperial army, fearsome monsters, and enemies within.
Lost Eidolons is a turn-based tactical RPG with a gripping cinematic narrative, set in a waning empire riven by civil war. Take on the role of a charming mercenary captain, Eden, and lead his band of allies through epic encounters on a classic turn-based battlefield.
The keyword return ends a function and passes a value to the caller.
The keyword break ends a loop immediately without doing anything else. It can be used within or outside a function.
return
break
Used to end a function
Used to end a for or while loop
Passes an optional value to the caller of the function (e.g., return 'hello')
Doesn’t pass anything to the “outside”
While they serve a different purpose, i.e., ending a function vs ending a loop, there are some cases where they can be used interchangeably.
Similar Use Cases
The following use case shows why you may have confused both keywords return and break. In both cases, you can use them to end a loop inside a function and return to the outside.
Here’s the variant using return:
def f(): for i in range(10): print(i) if i>3: return f()
And here’s the variant using break:
def f(): for i in range(10): print(i) if i>3: break f()
Both code snippets do exactly the same—printing out the first 5 values 0, 1, 2, 3, and 4.
Output:
0
1
2
3
4
However, this is where the similarity between those two keywords ends. Let’s dive into a more common use case where they both perform different tasks in the code.
Different Use Cases
The following example uses both keywords break and return. It uses the keyword break to end the loop as soon as the loop variable i is greater than 3.
So the line print(i) is never executed after variable i reaches the value 4—the loop ends.
But the function doesn’t end because break only ends the loop and not the function. That’s why the statement print('hi') is still executed, and the return value of the function is 42 (which we also print in the final line).
def f(): for i in range(10): if i>3: break print(i) print('hi') return 42 print(f())
Output:
0
1
2
3
hi
42
Summary
The keyword return is different and more powerful than the keyword break because it allows you to specify an optional return value. But it can only be used in a function context and not outside a function.
You use the keyword return to give back a value to the caller of the function or terminate the whole function.
You use the keyword break to immediately stop a for or whileloop.
Rule: Only if you want to exit a loop inside a function and this would also exit the whole function, you can use both keywords. In that case, I’d recommend using the keyword return instead of break because it gives you more degrees of freedom, i.e., specifying the return value. Plus, it is more explicit which improves the readability of the code.
Thanks for reading over the whole tutorial—if you want to keep learning, feel free to join my email academy. It’s fun!