I've scripted my way into a corner where I now need to compare the values of a hash to the corresponding element in an array.
I have two "lists" of the same values, sorted in different ways, and they should be identical. The interesting part is when they don't, so I need to identify those cases. So basically I need to check whether the first value of the first key-value pair in the hash is identical to the first element of the array, and likewise the second value checked towards the second element and so on for the entire set of values in the hash.
I'm sort of new to Ruby scripting, but though this should be easy enough, but alas....
Sounds like all you need is something simple like:
hash.keys == array
The keys should come out in the same order as they are in the Hash so this is comparing the first key of the hash with the first element of the array, the second key with the second array element, ...
You could also transliterate what you're saying into Ruby like this:
hash.each_with_index.all? { |(k, _), i| k == array[i] }
Or you could say:
hash.zip(array).all? { |(k, _), e| k == e }
The zip version is pretty much the each_with_index version with the array indexing essentially folded into the zip.
Technically speaking, hash is not guaranteed to ordered, so your assumption of matching the value at 'each' index of hash may not always hold true. However, for sake of answering your question, assuming h is your hash, and a is your array:
list_matches = true
h.values.each_with_index {|v, i| list_matches = list_matches && a[i] == v}
if list_matches is not equal to true than that is where the the items in the two collections don't match.
Related
my array have is
let arr=['20336.41905.32121.58472_20336.41905.60400.51092_1',
'20336.41905.32121.58472_20336.41905.60400.48025_2',
'20336.41905.32121.58472_20336.41905.41816.60719_3',
'20336.41905.32121.58472_20336.41905.41816.63631_4',
'20336.41905.32121.58472_20336.41905.31747.22942_2',
]
want to get sort as an order like this
['20336.41905.32121.58472_20336.41905.60400.51092_1',
'20336.41905.32121.58472_20336.41905.60400.48025_2',
'20336.41905.32121.58472_20336.41905.31747.22942_2',
'20336.41905.32121.58472_20336.41905.41816.60719_3',
'20336.41905.32121.58472_20336.41905.41816.63631_4',
]
We can try sorting using a lambda expression:
var arr = ['20336.41905.32121.58472_20336.41905.60400.51092_1',
'20336.41905.32121.58472_20336.41905.60400.48025_2',
'20336.41905.32121.58472_20336.41905.41816.60719_3',
'20336.41905.32121.58472_20336.41905.41816.63631_4',
'20336.41905.32121.58472_20336.41905.31747.22942_2',
];
arr.sort((a, b) => parseInt(a.split(/_(?!.*_)/)[1]) - parseInt(b.split(/_(?!.*_)/)[1]));
console.log(arr);
The logic used above is to split on the final underscore of each array value, and parse the right side number to an integer. Then we do a comparison of each pair of numbers to achieve the sort.
Assuming you are using JavaScript...
The sort function can take a callback as an argument. You can use this to determine the criteria by which you desire to sort your array.
When comparing 2 values compare a to b, the function should return a number greater than zero if a goes after b, less than zero if a goes before b, and exactly zero if any could follow the other (ie. 1.10 and 1.100 could be sorted as [1.10, 1.00] or [1.100, 1.00] because for all we care they have the same value, on your particular case, 2 array elements ending in 4 would follow the same principle because that is the only number in our criteria).
An example would be:
arr.sort((a, b)=>{
return parseInt(a.slice(-1)) - parseInt(b.slice(-1))
})
Note that this will only work if the last character on every element of your array is a numeric character and would not ever care about the second to last character if 2 last characters are equal.
There is an ugliest solution too that could work, although I don't really recommend it, it would take into consideration all characters in reverse order.
Map over all elements, reverse them, sort the array without using a callback (arr.sort()), and then reverse all elements again.
I have a mtd to search through 1mil or more records (stored as an arraylist of integers in asc order) to check if the pass in empID belongs to one of the records stored.
Currently, i uses sequential search through for loop. How to make it more efficient/faster?
def exist?(id)
for i in 0...$employee_list.length
if $employee_list[i] == id # match!
return true
elsif $employee_list[i] > id # have already gone beyond the point where id should've been found
return false
end
end
return false # cannot find id in the list
end
I also tried using hash as follows but still not fast enough.
hash = $employee_list.map{ |i | i}
if hash.include? id
return true
else
return false
end
Use a Set, unless you have proven that you cannot afford the memory:
# Do this just once
require 'set'
$employee_ids = Set.new $employee_list
# Do this each time you need to check
def exist?(id)
$employee_ids.include?(id)
end
It will be nearly instantaneous, regardless of the number of ids you have.
If you can't use a Set instead of an Array (for space reasons), and if your Array is sorted, you can use Array#bsearch with a block that returns an integer (like <=>).
Try this
array.bsearch {|x| number <=> x }
This does a binary search on the array. The array MUST be sorted.
Note that the element x is to the right side of the spaceship operator!
Use the ri command to read more documentation on the bsearch method. The time complexity of binary search is O(log n). Which is 20 steps only for an array of length 1 million.
I was trying to initiate a hash that have 7 NAs as values.
This is what I have:
values %hash = ("NA") x 7;
print join("\t",values %hash),"\n";
I got this error:
Can't modify values in scalar assignment at line 22, near "7;"
Apparently, I can't do this with values of hash although I can assign array to hash keys
keys %hash = ["a","b","c","d","e","f","g"];
Why is it working for keys but not values for hash assignment?
From perldoc -f keys:
Used as an lvalue, "keys" allows you to increase the number of hash buckets allocated for the given hash. This can gain you a measure of efficiency if you know the hash is going to get big.
I.e. this method is not useful to set the keys, only to allocate space for a certain number of entries. When using a reference as the number, the result will probably be something ridiculously large that will eat most of your memory – not exactly recommended.
To initialize a hash with some values, you have to specify the required keys. But you can use a slice as an lvalue:
my %hash;
#hash{1..7} = ("NA") x 7;
Note: an lvalue is a value that can be used on the left side of an assignment.
A hash has two parts to it, keys and values. e.g.:
my %hash = ( a => 1, b => 2, c => 3 );
Here, the keys are 'a', 'b' and 'c'. The values are 1, 2 and 3.
If you look at what keys and values do, they (unsurprisingly) return the keys and values of the hash respectively.
They are not meant to set the values or keys of a hash, merely to retrieve.
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
Is there a way to evaluate the order of an array? I want to find an array of records ordered by created_at date and then see if the numbers in that array are in sequence?
For instance:
Model.all.order(&:created_at).select("lesson_number)
[1, 2, 4, 3, 5]
should fail because the numbers are not in sequence
I could execute two finds. One that is ordered by "lesson_number" and one that is ordered by created_at date. Convert them both the a string then compare the two. But, seems like a lot of work if a rails method exists to handle such a thing.
You can combine a couple methods in Ruby to do this pretty efficiently.
input.each_cons(2).reduce(true) { |result, (a, b)| result && (a <=> b) < 0 }
each_cons will iterate through your array yielding, in this case, each 2 consecutive items. Because we don't pass it a block, it returns an enumerator that we can iterate through and get a single resulting value using reduce (a.k.a. inject).
Our block compares a and b using <=> which will return -1, 0, or 1 depending on whether the first value is "less-than", equal, or "greater than". In this case, we want to make sure a is -1.
In case you're not familiar with it, the parenthesis in the block argument are Ruby 1.9+, and they allow the arguments to be splatted in (otherwise we would get a 2-item array in our block).