Best way to reduce a matcher - algorithm

I have a matcher like so:
type matcher struct {
all []int
none []int
any [][]int
}
For an array of integers to match, it must contain all of the integers in all, none of the integers in none, and must contain at least one integer from each array of integers in any. I have a factory to make the construction of such a matcher easy, but once I have the factory fields I need to "flatten" the structure to make sure none of the terms are contradictory. Right now, what I have is (pseudocode):
all = [0, 1, 2, 3] // means all of these numbers must be present for the matcher to match
any = [[4, 5], [6, 7, 8]] // means one number from each of these groups must be present
none = [9,10] // means none of these may be present
sort(all)
sort(none)
if len(intersections(all, none)) != 0 {
panic("All and none are contradictory!")
}
for gIndex, group in any {
sort(group)
for index, integer in group {
// If all contains the integer...
if binarySearch(all, integer) {
swap(group[len(group)-1], group[index]) // swap the last item with the integer...
group = group[:len(all)-1] // and truncate the group by one; most efficient way to delete an item.
}
// If none contains the integer...
if binarySearch(none, integer) {
panic("item in any is also found in none; possible contradiction")
}
}
if len(group) < 1 {
// delete group from any
} else if len(group) == 1 {
// put group[0] (only integer in it) into all, since it must be met
} else {
sort(group) // Re-sort the group
any[gIndex] = group // Update the group
}
}
removeDuplicates(all)
removeDuplicates(none)
sort2D(any) // Sort by their greatest member
removeDuplicates2D(any) // Go through and remove any duplicate arrays.
all, any, and none should now be reduced to their simplest form, so that checking if an integer array matches should be more efficient and without contradictions.
Is there a better way to do this, and am I missing anything here?

There are at least two flaws in your solution.
You are removing integers in all from the any groups. In fact you have to remove the whole group instead of removing an item if there is an intersection with all. any groups containing items from all are just redundant.
Having overlaps with the none group is not necessarily a problem. These items could in fact just be removed from the any groups. But then you might want to throw an error when this leads to an empty any group. A matcher with an empty any group won't match anything. Anyway, don't just quietly remove empty any groups!
Something similar applies for any groups subsuming other any groups. Consider this choice of any:
any = [[4, 5], [4, 5, 6]]
Let's assume that all and none are empty. Then this would remain unchanged by your algorithm. However, the second group is clearly redundant. Any array containing at least one element of the first group will also match the second.
This means that at least you have to remove all any arrays containing a superset of others.
I'm confident that this leads to a canonical form that you can use to compare matchers. However, I may be wrong.

Related

How can I return hash pairs of keys that sum up to less than a maximum value?

Given this hash:
numsHash = {5=>10, 3=>9, 4=>7, 2=>5, 20=>4}
How can I return the key-value pair of this hash if and when the sum of its keys would be under or equal to a maximum value such as 10?
The expected result would be something like:
newHash = { 5=>10, 3=>9, 2=>5 }
because the sum of these keys equals 10.
I've been obsessing with this for hours now and can't find anything that leads up to a solution.
Summary
In the first section, I provide some context and a well-commented working example of how to solve the defined knapsack problem in a matter of microseconds using a little brute force and some Ruby core classes.
In the second section, I refactor and expand on the code to demonstrate the conversion of the knapsack solution into output similar to what you want, although (as explained and demonstrated in the answer below) the correct output when there are multiple results must be a collection of Hash objects rather than a single Hash unless there are additional selection criteria not included in your original post.
Please note that this answer uses syntax and classes from Ruby 3.0, and was specifically tested against Ruby 3.0.3. While it should work on Ruby 2.7.3+ without changes, and with most currently-supported Ruby 2.x versions with some minor refactoring, your mileage may vary.
Solving the Knapsack Problem with Ruby Core Methods
This seems to be a variant of the knapsack problem, where you're trying to optimize filling a container of a given size. This is actually a complex problem that is NP-complete, so a real-world application of this type will have many different solutions and possible algorithmic approaches.
I do not claim that the following solution is optimal or suitable for general purpose solutions to this class of problem. However, it works very quickly given the provided input data from your original post.
Its suitability is primarily based on the fact that you have a fairly small number of Hash keys, and the built-in Ruby 3.0.3 core methods of Hash#permutation and Enumerable#sum are fast enough to solve this particular problem in anywhere from 44-189 microseconds on my particular machine. That seems more than sufficiently fast for the problem as currently defined, but your mileage and real objectives may vary.
# This is the size of your knapsack.
MAX_VALUE = 10
# It's unclear why you need a Hash or what you plan to do with the values of the
# Hash, but that's irrelevant to the problem. For now, just grab the keys.
#
# NB: You have to use hash rockets or the parser complains about using an
# Integer as a Symbol using the colon notation and raises SyntaxError.
nums_hash = {5 => 10, 3 => 9, 4 => 7, 2 => 5, 20 => 4}
keys = nums_hash.keys
# Any individual element above MAX_VALUE won't fit in the knapsack anyway, so
# discard it before permutation.
keys.reject! { _1 > MAX_VALUE }
# Brute force it by evaluating all possible permutations of your array, dropping
# elements from the end of each sub-array until all remaining elements fit.
keys.permutation.map do |permuted_array|
loop { permuted_array.sum > MAX_VALUE ? permuted_array.pop : break }
permuted_array
end
Returning an Array of Matching Hashes
The code above just returns the list of keys that will fit into your knapsack, but per your original post you then want to return a Hash of matching key/value pairs. The problem here is that you actually have more than one set of Hash objects that will fit the criteria, so your collection should actually be an Array rather than a single Hash. Returning only a single Hash would basically return the original Hash minus any keys that exceed your MAX_VALUE, and that's unlikely to be what's intended.
Instead, now that you have a list of keys that fit into your knapsack, you can iterate through your original Hash and use Hash#select to return an Array of unique Hash objects with the appropriate key/value pairs. One way to do this is to use Enumerable#reduce to call Hash#merge on each Hash element in the subarrays to convert the final result to an Array of Hash objects. Next, you should call Enumerable#unique to remove any Hash that is equivalent except for its internal ordering.
For example, consider this redesigned code:
MAX_VALUE = 10
def possible_knapsack_contents hash
hash.keys.reject! { _1 > MAX_VALUE }.permutation.map do |a|
loop { a.sum > MAX_VALUE ? a.pop : break }; a
end.sort
end
def matching_elements_from hash
possible_knapsack_contents(hash).map do |subarray|
subarray.map { |i| hash.select { |k, _| k == i } }.
reduce({}) { _1.merge _2 }
end.uniq
end
hash = {5 => 10, 3 => 9, 4 => 7, 2 => 5, 20 => 4}
matching_elements_from hash
Given the defined input, this would yield 24 hashes if you didn't address the uniqueness issue. However, by calling #uniq on the final Array of Hash objects, this will correctly yield the 7 unique hashes that fit your defined criteria if not necessarily the single Hash you seem to expect:
[{2=>5, 3=>9, 4=>7},
{2=>5, 3=>9, 5=>10},
{2=>5, 4=>7},
{2=>5, 5=>10},
{3=>9, 4=>7},
{3=>9, 5=>10},
{4=>7, 5=>10}]

How do I sort Discord.Collection by keys?

I have a Discord.Collection of key-value pairs that stores the information about the number of commands in folders. It looks something like this:
debug - 2
utility - 2
fun - 3
best - 4
replies - 3
I want to sort the collection by folder names (keys) alphabetically (ascending). However, Discord.Collection.sort() sorts ascending by values, meaning my output is debug, utility, fun, replies, best instead of desired best, debug, fun, replies, utility output. Because Discord.Collection extends Map, I looked up js map documentation, but there is no .sort() method. I also looked up sorting in StackOverflow and google, but I only found answers regarding value sorting (or answer in different coding language, which I failed to translate).
I know .sort() accepts lambda expression as parameter, but I don't know how to use it for key sorting - I only ever used it for value sorting.
I don't think the code is necessary for this question, but in case you need to visualise my problem, here is creation of my collection:
const FolderCollection = new Discord.Collection();
//commandList is a Collection of key: command.name, value: {command: command, folder: folder}
commandList.each((cmd) => {
if (FolderCollection.has(cmd.folder)) {
FolderCollection.set(
cmd.folder,
FolderCollection.get(cmd.folder) + 1
);
} else {
FolderCollection.set(cmd.folder, 1);
}
});
Here I want to use my sorted FolderCollection:
FolderCollection.sort().each((cmdCount, categoryName) => {
//...do stuff
});
Feel free to ask for any additional details, but I believe this is all you need to help me with my issue (either direct answer, or link to documentation).
You could use Collection#entries() in a spread operator to turn the collection into an array with the structure [[key, value]], sort that because you'll have access to the whole array (key and value), and then convert it back into a collection if needed. Here's an example with a Map.
// let's say my objective is to sort these
// from lowest to highest relative to the key
// so the result should be:
// Map (3) { 1 => 'bar', 2 => 'baz', 3 => 'foo' }
const map = new Map([[3, 'foo'], [1, 'bar'], [2, 'baz']]);
// make use of [...iterable] to turn an iterable
// into an array
const entries = [...map.entries()];
console.log('Entries: ', entries);
// use array destructuring to access the
// first element of both arrays
entries.sort(([a], [b]) => a - b);
console.log('Sorted Entries: ', entries);
// convert it back to a map (or collection)
// if needed
const newMap = new Map(entries);

Can I count on partition preserving order?

Say I have a sorted Array, such as this:
myArray = [1, 2, 3, 4, 5, 6]
Suppose I call Enumerable#partition on it:
p myArray.partition(&:odd?)
Must the output always be the following?
[[1, 3, 5], [2, 4, 6]]
The documentation doesn't state this; this is what it says:
partition { |obj| block } → [ true_array, false_array ]
partition → an_enumerator
Returns two arrays, the first containing the elements of enum for which the block evaluates to true, the second containing the rest.
If no block is given, an enumerator is returned instead.
But it seems logical to assume partition works this way.
Through testing Matz's interpreter, it appears to be the case that the output works like this, and it makes full sense for it to be like this. However, can I count on partition working this way regardless of the Ruby version or interpreter?
Note: I made implementation-agnostic because I couldn't find any other tag that describes my concern. Feel free to change the tag to something better if you know about it.
No, you can't rely on the order. The reason is parallelism.
A traditional serial implementation of partition would loop through each element of the array evaluating the block one at a time in order. As each call to odd returns, it's immediately pushed into the appropriate true or false array.
Now imagine an implementation which takes advantage of multiple CPU cores. It still iterates through the array in order, but each call to odd can return out of order. odd(myArray[2]) might return before odd(myArray[0]) resulting in [[3, 1, 5], [2, 4, 6]].
List processing idioms such as partition which run a list through a function (most of Enumerable) benefit greatly from parallel processing, and most computers these days have multiple cores. I wouldn't be surprised if a future Ruby implementation took advantage of this. The writers of the API documentation for Enumerable likely carefully omitted any mention of process ordering to leave this optimization possibility open.
The documentation makes no explicit mention of this, but judging from the official code, it does retain ordering:
static VALUE
partition_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arys))
{
struct MEMO *memo = MEMO_CAST(arys);
VALUE ary;
ENUM_WANT_SVALUE();
if (RTEST(enum_yield(argc, i))) {
ary = memo->v1;
}
else {
ary = memo->v2;
}
rb_ary_push(ary, i);
return Qnil;
}
This code gets called from the public interface.
Essentially, the ordering in which your enumerable emits objects gets retained with the above logic.

How to assign more than one value to UInt32

I am trying to set the bird group as two numbers so that when I assign a variable I can use multiple "else if" statements with that one group later on
Code:
Xcode doesn't let me do this I'm in Swift
Let birdgroup: UInt32 = 2, 3
You can use Array, Set, or a tuple to store multiple values in a single variable. If order matters, go with Array or tuple, but if the order doesn't matter, you can use Set. Array and Set both allow you to vary the number of values stored in your variable, while a tuple variable must always be the same length. Also, you can loop over the items in an array or set, but not over a tuple.
Array is the most often used of the three, so if you aren't sure which to use, it's a good first choice.
In summary, this table shows the possibilities and their properties:
Loopable Unloopable
Ordered Array Tuple
Unordered Set (none)
Finally, all the items in an array or set must be of the same type (or derived from the same type, if the array or set is defined with the base class). This is called homogeneous. A tuple can contain different types, also known as heterogeneous.
Homogeneous Heterogeneous
Ordered Array Tuple
Unordered Set (none)
Collection Types in the Swift documentation describes how to use Array and Set.
Array
Create an array with
var birdgroup: [UInt32] = [2, 3]
birdgroup[0] is equal to 2, and birdgroup[1] is equal to 3. You can also access the items by looping:
for bird in birdgroup {
println("\(bird)")
}
Set
You can declare a set with
var birdgroup: Set<UInt32> = [2, 3]
Because sets have no order (imagine every item is tossed together in a bag), you can't request the "first" or "second" item. Instead, loop over each item of the set:
for bird in birdgroup {
println("\(bird)")
}
Tuple
let birdgroup: (UInt32, UInt32) = (2, 3)
Tuples also retain the order of their items. birdgroup.0 is equal to 2, and birdgroup.1 to 3. You can also give each item of the tuple a name if you prefer that to a number:
let birdgroup: (UInt32, UInt32) = (foo: 2, bar: 3)
birdgroup.foo is 2, and birdgroup.bar is 3.
Additionally, the values in a tuple do not all need to be the same type. You can combine different types, such as
let heterogeneousTuple: (UInt32, String) = (2, "three")

Parse.com query objects where the key's array value contains any of the elements

on https://parse.com/docs/js_guide#queries-arrays there is an example how to find objects where the key's array value contains each of the elements 2, 3, and 4 with the following:
// Find objects where the array in arrayKey contains all of the elements 2, 3, and 4.
query.containsAll("arrayKey", [2, 3, 4]);
However, I would like to find objects where the key's array value contains at least one (not necessarily all) of the elements 2,3, and 4.
Is that possible?
I'm not positive, but what happens if you try containedIn?
I think if you pass an array, it checks to see if any are contained.
query.containedIn("arrayKey", [2,3,4]);
I know that if you use equalTo with an array key and a singular value, it checks if the value is in the array and returns TRUE. I think this will do something similar and should work. I think it will check if any value in "arrayKey" is in the passed array. If any key object does, it will return the object.
swift 3.0
let Query:PFQuery = PFQuery(className: “className”)
Query.whereKey(“Field Name”, containedIn: array)// [“1”,”2”,”3”];

Resources