How to get the maximum value from hash - arrays

I want to get the maximum value from the hash but i keep getting the first one
Crypto_name = ["Bitcoin", "Ethereum", "Project-x", "Fake"]
Crypto_price = ["$5500.6", "$538.9", "$0.25", "$46000.09"]
$crypto = Crypto_name.zip(Crypto_price).to_h
def largest_hash_key
puts $crypto.max_by{|a,b| b.to_i}
end
largest_hash_key

As per the format of each string within crypto_price, you'd need to remove any non-digit character, with those 4 elements, $ is the one you don't need.
max can help you:
$crypto.max { |value| value.last.delete('$').to_f }
# "$46000.09"
By removing the $ you get a "convertible" to float number, which you can then compare.
I blindly assume that "$" is the only one character that's not needed among them. But it won't be always this way.

Perhaps
$crypto.max_by{|a,b| b.tr('^0-9''.','').to_f}
I think this works with any symbols and in any order.

Related

How remove comma in array of string to convert in array of float

I have an array of strings:
my_array = ["$8,579.06", "$166.37", "$0.226463", "$346.31", "$275.27"]
I want to get a new array:
my_array = [8,579.06, 166.37, 0.226463, 346.31, 275.27]
I want to remove the $ and convert the values into floats, so I did this:
my_array.map! { |v| v.delete_prefix("$").to_f}
But that returned:
my_array = [8.0, 166.37, 0.226463, 346.31, 275.27]
I think it's because of the comma but my array is huge.
How do I remove all the commas of each value without removing the ones separating the different indexes in my array?
I think this goes according to the docs:
... Extraneous characters past the end of a valid number are ignored...
You're getting just "8.0" because all the remaining characters after the 8 are ignored, as the comma can not be interpreted as a floating-point number. Which would be the same that doing "8".to_f and gives 8.0.
To avoid that you can try removing the commas per element inside the array:
["$8,579.06", "$166.37", "$0.226463", "$346.31", "$275.27"].map { |e| e.delete_prefix("$").delete(',').to_f }
# [8579.06, 166.37, 0.226463, 346.31, 275.27]
I'd do this:
ary = ["$8,579.06", "$166.37", "$0.226463", "$346.31", "$275.27"]
ary.map { |i| i.delete('$,').to_f }
# => [8579.06, 166.37, 0.226463, 346.31, 275.27]
delete is the magic part. It strips any characters named in the parameter from the string.
Commas in the viewable/screen display of numbers to denote thousands are a human artifice, to make it easier for us to gauge the magnitude of the number. Computers don't need them.
Assign the returned value from map to a variable or chain to it.
And, as a heads-up, be aware that different regions around the world use two different ways to denote thousands and the decimal point. Some use 1,000.00 and some are 1.000,00. Assuming that a string representation of currency has commas for thousands and a period for the fractional part is the source of a bug, quite a major bug if you're dealing with money, especially if you're doing financial calculations for loans. In your example you could write code to determine the values are U.S. based because of $, but without that you'd have a harder time telling. So if this is something you anticipate having to do I'd strongly recommend researching the problems and solutions.

how to use RANDOM command in declared array

I am trying to build a script where I need to create a password generator with the following parameters :
must be at least 8 characters long but not longer than 16 characters.
must contain at least 1 digit (0-9).
must contain at least 1 lowercase letter.
must contain at least 1 uppercase letter.
must contain exactly one and only one of # # $ % & * + - =
I have two ideas :
The first :
#!/bin/bash
#
#Password Generator
#
#
Upper=('A''B''C''D''E''F''G''H''I''J'K''L''M''N''O''P''Q''R''S''T''U''V''W''X''Y''Z')
Lower=('a''b''c''d''e''z''f''g''h''i''j''k''l''m''o'''p''q''r''s''t''u''v''w''x''y''z')
Numbers=('1''2''3''4''5''6''7''8''9')
SpecialChar=('#''#''$''%''&''*''+''-''=')
if [ S# -eq 0 ] ; then
Pwlength=`shuf -i 8-16 -n 1`
Password=`< /dev/urandom tr -dc A-Za-z0-9$SpecialChar | head -c $Pwlength`
echo "Random Password is being generated for you"
sleep 5
echo "Your new password is : $Password"
exit
The problem is I get characters that I didnt even defined ?
The secound idea :
for((i=0;i<4;i++))
do
password=${Upper[$random % ${#Lower[#]} ] }
password=${Upper[$random % ${#Upper[#]} ] }
password=${Upper[$random % ${#Number[#]} ] }
password=${Upper[$random % ${#SpecialChar[#]} ] }
done
For some reason non of them work ;/
In your first example, move the "-" character to the end of the SpecialChar. I think the definition as you had it results in allowing "+" to "=" (i.e., the value passed to tr reads as "+-="), which accounts for the characters you were not expecting. Alternatively, replace the "-" with "_".
So, try a definition like:
SpecialChar=('#''#''$''%''&''*''+''=''-')
As already mentioned, it would be cleaner and easier to use directly a string to handle list of characters. Handling special characters in an array may cause side effects (for instance getting the list of files in the current directory with the '*' character). In addition, arrays may be difficult to pass as function arguments).
ALPHA_LOW="abcdefghijklmnopqrstuvwxyz"
# Then simply access the char in the string at the ith position
CHAR=${ALPHA_LOW:$i:1}
You could generate upper cases from the lower cases.
ALPHA_UP="`echo \"$ALPHA_LOW\" | tr '[:lower:]' '[:upper:]'`"
The variable which contains a random number is $RANDOM (in capital letters).
sleep 5 is unnecessary.
You need to find a way to keep count of occurrences left for each character type. For more information, I wrote a complete script to do what you described here.
Your first attempt has the following problems:
You are using arrays to contain single strings. Pasting 'A''B''C' is equivalent to 'ABC'. Simply converting these to scalar variables would probably be the simplest fix.
You are not quoting your variables.
You declare Upper, Lower, and Numbers, but then never use them.
Using variables for stuff you only ever use once (or less :-) is dubious anyway.
As noted by #KevinO, the dash has a special meaning in tr, and needs to be first or last if you want to match it literally.
On top of that, the sleep 5 doesn't seem to serve any useful purpose, and will begin to annoy you if it doesn't already. If you genuinely want a slower computer, I'm sure there are people here who are willing to trade.
Your second attempt has the following problems:
The special variable $RANDOM in Bash is spelled in all upper case.
You are trying to do modulo arithmetic on (unquoted!) arrays of characters, which isn't well-defined. The divisor after % needs to be a single integer. You can convert character codes to integers but it's sort of painful, and it's less than clear what you hope for the result to be.
A quick attempt at fixing the first attempt would be
Password=$(< /dev/urandom tr -dc 'A-Za-z0-9##$%&*+=-' | head -c $(shuf -i 8-16 -n 1))
If you want to verify some properties on the generated password, I still don't see why you would need arrays.
while read -r category expression; do
case $Password in
*[$expression]*) continue;;
*) echo "No $category"; break;;
esac
done <<'____HERE'
lowercase a-z
uppercase A-Z
numbers 0-9
specials -##$%&*+=
HERE

Roman Number Calculator

I have to make an assignment wherein I have to implement the Roman Numerals along with the rules that are followed while doing calculations on them, contained in this link
http://www.periodni.com/roman_numerals_converter.html
I don't want the code, just help. I have gone with two approaches till now:
Using constants, I have declared the roman numerals as constants, and then after getting the input, I am left with the choice to decode the string input, which I later dropped.
Using hash. I have declared a hash with the numerals, then after getting the input, I am converting the string using to_sym method into there corresponding value. After this, I have taken an array containing the decimals of the roman numerals, and I intend to compare those with the input.
I am stuck at this part. I don't know how to compare here. Also, I don't want to sort the input array, because then calculations would go wrong.
Can anyone help me with how to proceed? Not the code, just steps.
This is what I have done so far:
class Conversion
hash ={ :I => 1, :V => 5, :X => 10, :L => 50, :C => 100, :D => 500, :M => 1000}
result = 0
value = []
hash_values = hash.values
puts "enter input string"
input = gets.chomp.upcase.split(//)
input.each do |i|
value <<  hash[i.to_sym]
end
for i in value do
if value[i] > value[i+1]
result = result + value[i]
else
result = result + (value[i+1] - value[i])
end
end
puts result
end
If u run the code, you'll see that while I try to compare in the last loop it is taking the index for calculations. Same happened when I tried doing the same using two hashes. I can't use any gem or external libraries cause that is the requirement.
The idea is to create a tree where every node have two childrens: the left one is what to substract and the right one is what to add.
Once you get a string, you find the most valued letter and make it the first node like this:
XLIX
L
/ \
X IX
Then you run this function recursively on the children nodes until the node is trivial (like XX or III).
The final step is to recursively calculate the result.

What is a regular expression for parsing key = value using C's regex.h lib

I need an expression which would much key=value line
Actually you might have between "key" and "=" "value" as many whitespaces as you want
so key = value is valid as well. But multivalue should not be allowed.
So something like this:
**key = value1 value2**
is not allowed.
I've already tried with
**const char* regexCheckValidityForKeyValue = "([[:print:]]{1,})([:blank:]*)(\\=){1}([[:blank:]]*)([[:graph:]]*)";**
But this does not really work.
Thank you for any help.
At least to me, it appears that using a regex for this is entirely unnecessary. Getting the code correct will be comparatively difficult, and reading it even more so.
I'd just use [sf]scanf:
if (2 == (sscanf(input, "%s = %s %s", key, value, ignore))
// it's good: just `key = value`
else
// malformed
Basically, this attempts to read and convert a key, a value, and a second value. It then checks the return value to see how many of those were matched. If exactly two matched, you have "key = value". If fewer than 2 matched, some part of key = value is missing. If it matches more than that, you have key = value1 value2 (and possibly more after value2), so that's malformed as well.
As a bonus, this also gives you the actual strings that made up your key and value without any extra cruft.
(Decided to put the comment as an answer instead...)
You're close - just put the first [:blank:] inside a character class as you've done with the rest of them. Then remove the escaping of the '=' i.e. : ([[:print:]]{1,})([[:blank:]]*)(=){1}([[:blank:]]*)([[:graph:]]*), or if your flavor supports shorthand - (\w+)\s*=\s*(\S+) which is a bit easier to read (or at least shorter ;)). Also captures only the key and the value, not the spaces and stuff that your regex does.
Regards
The "print" class includes the space character 0x20-0x7e, which is making the key allowed to have spaces, or basically could match all spaces as a key.
I assume this is extended POSIX.
There are problems in your description.
The only way to not allow a second 'value2' is to put edge conditions at the end.
But, not much is known about that.
modified Since there is no way to negate a posix class, you have to be specific to stop part delimeters from being included in key/value.
# ^[[:blank:]]*([[:alpha:]][!-<>-~]*)[[:blank:]]*=[[:blank:]]*([!-<>-~]+)[[:blank:]]*$
^ # BOS
[[:blank:]]* # space or tab
( # (1 start), KEY
[[:alpha:]] # start with alpha char
[!-<>-~]* # any chars not equal sign nor whitespace
) # (1 end)
[[:blank:]]* # spaces or tabs
= # equal sign
[[:blank:]]* # spaces or tabs
( [!-<>-~]+ ) # (2), VALUE any chars not equal sign nor whitespace
[[:blank:]]* # spaces or tabs
$ # EOS

Algorithm - check if any string in an array of strings is a prefix of any other string in the same array

I want to check if any string in an array of strings is a prefix of any other string in the same array. I'm thinking radix sort, then single pass through the array.
Anyone have a better idea?
I think, radix sort can be modified to retrieve prefices on the fly. All we have to do is to sort lines by their first letter, storing their copies with no first letter in each cell. Then if the cell contains empty line, this line corresponds to a prefix. And if the cell contains only one entry, then of course there are no possible lines-prefices in it.
Here, this might be cleaner, than my english:
lines = [
"qwerty",
"qwe",
"asddsa",
"zxcvb",
"zxcvbn",
"zxcvbnm"
]
line_lines = [(line, line) for line in lines]
def find_sub(line_lines):
cells = [ [] for i in range(26)]
for (ine, line) in line_lines:
if ine == "":
print line
else:
index = ord(ine[0]) - ord('a')
cells[index] += [( ine[1:], line )]
for cell in cells:
if len(cell) > 1:
find_sub( cell )
find_sub(line_lines)
If you sort them, you only need to check each string if it is a prefix of the next.
To achieve a time complexity close to O(N2): compute hash values for each string.
Come up with a good hash function that looks something like:
A mapping from [a-z]->[1,26]
A modulo operation(use a large prime) to prevent overflow of integer
So something like "ab" gets computed as "12"=1*27+ 2=29
A point to note:
Be careful what base you compute the hash value on.For example if you take a base less than 27 you can have two strings giving the same hash value, and we don't want that.
Steps:
Compute hash value for each string
Compare hash values of current string with other strings:I'll let you figure out how you would do that comparison.Once two strings match, you are still not sure if it is really a prefix(due to the modulo operation that we did) so do a extra check to see if they are prefixes.
Report answer

Resources