Parse 2d arrays in an optimal way - arrays

I need to parse the following hash of 2d arrays, where the first array has the keys and the rest of the arrays has the values.
input = {
"result": [
[
"id",
"name",
"address"
],
[
"1",
"Vishnu",
"abc"
],
[
"2",
"Arun",
"def"
],
[
"3",
"Arjun",
"ghi"
]
]
}
This is the result I came up with.
input[:result].drop(1).collect{|arr| Hash[input[:result].first.zip arr]}
Here I'm iterating through the result array ignoring its first sub array (the one that contains keys) then zip the key array and value array to make a hash afterwards I collect the hash to another array.
The above solution gives me what I want which is a hash
[{"id"=>"1", "name"=>"Vishnu", "address"=>"abc"}, {"id"=>"2", "name"=>"Arun", "address"=>"def"}, {"id"=>"3", "name"=>"Arjun", "address"=>"ghi"}]
Is there a better way to achieve the same result?

zip is the correct tool here, so your code is fine.
I'd use Ruby's array decomposition feature to extract keys and values, and to_h instead of Hash[]:
keys, *values = input[:result]
values.map { |v| keys.zip(v).to_h }
Or, if you prefer a "one-liner": (harder to understand IMO)
input[:result].yield_self { |k, *vs| vs.map { |v| k.zip(v).to_h } }

Related

Sort array of arrays by hash key in value and reinsert key

I'd like to reorder an array of arrays. By default, the array is sorted by the English keys:
english_sorted_terms = [
[
"A1_english", {"domains"=>[], "en"=>{"name"=>"A_english", "def"=>"A"}, "de"=>{"name"=>"Z_german", "def"=>"..." }}
],
[
"Z1_english", {"domains"=>[], "en"=>{"name"=>"Z_english", "def"=>"Z"}, "de"=>{"name"=>"A_german", "def"=>"..."}}
]
]
After sorting, 'Z1_english' should be the first element because it contains the name 'A_german'.
My other attempts do correct the 'de' sub-key but I cannot reinsert the right parent key or the key but the sorting isn't right
english_sorted_terms
.map { |term| term[1] }
.sort_by { |key| key["de"]['name'] }
english_sorted_terms
.map { |term| [term[1]].sort_by! { |key| key["de"]['name'] }.unshift term[0] }
.sort_by! { |key| key["de"]['name'] } is working but it removes the first array element.
Ruby sorting works by comparing elements and you can define the comparison criteria yourself. In this example you compare with original array item's second element's ['de']['name']. For example:
english_sorted_terms.sort { |a, b| a.second['de']['name'] <=> b.second['de']['name'] }
See https://apidock.com/ruby/Array/sort.

Mongo: Negative array references with indexed elements possible?

I have added some array data to a Mongo DB using PyMongo. I've also created indexes for the array elements. The general approach is shown below.
data = [
{"d": ["a", "b", "c"],},
{"d": ["a", "c", "b"],},
{"d": ["b", "a", "c"],},
]
my_collection.insert_many(data)
my_collection.create_index('d.0')
my_collection.create_index('d.1')
my_collection.create_index('d.2')
The last element in the arrays can be matched by running,
[x for x in my_collection.find({"d.2": "c"})]
As I understand things, the below command will show that the indexes are being used.
my_collection.find({"d.2": "c"}).explain()
I would like reference array elements with negative integers. For example,
[x for x in my_collection.find({"d.-1": "c"})]
The above command doesn't work. However the following equivalent command will produce the desire results.
[x for x in my_collection.find({"$expr": {"$eq": [ { "$arrayElemAt": [ "$d", -1 ] }, "c" ] }})]
However, running,
my_collection.find({"$expr": {"$eq": [ { "$arrayElemAt": [ "$d", -1 ] }, "c" ] }}).explain()
seems to indicate that indexes will not be used. Is there a way to use both negative array references and indexes?

How to match an array in order with intervening elements using a MongoDB aggregation

I have documents with an array of events objects :
{
"events": [
{
"name": "A"
},
{
"name": "C"
},
{
"name": "D"
},
{
"name": "B"
},
{
"name": "E"
}
]
},
{
"events": [
{
"name": "A"
},
{
"name": "B"
},
{
"name": "S"
},
{
"name": "C"
}
]
}
]
In this array, I want to count the number of events that are in a said order, with intervening events. For example, I look for the order [A,B,C], with the array [A,x,x,B,x], I should count 2, with [A,B,x,x,C] I should have 3. (x is just a placeholder for anything else)
I want to summarize this information for all my documents in the shape of an array, with the number of matches for each element. With the previous example that would give me [2,2,1], 2 matches for A, 2 matches for B, 1 match for C.
My Current aggregation is generated in javascript and follow this pattern :
Match documents with the event array containing A
Slice the array from A to the end of the array
Count the number of documents
Append the count of matching document to the summarizing array
Match documents with the event array containing B
Slice the array from B to the end of the array
Count the number of documents
etc
However, when an event does not appear in any of the arrays, it falls shorts, as there are no documents, I do not have a way to store the summarizing array. For example, with the events array [A,x,x,B,x] [A,B,x,x,C] and trying to match [A,B,D], I would expect [2,2,0], but I have [] as when trying to match D nothing comes up, and the aggregation cannot continue.
Here is the aggregation I'm working with : https://mongoplayground.net/p/rEdQD4FbyC4
change the matching letter l.75 to something not in the array to have the problematic behavior.
So is there a way to not lose my data when there is no match? like bypassing aggregation stages, I could not find anything related to bypassing stages in the mongoDB documentation.
Or are you aware of another way of doing this?
We ended using a reduce to solve our problem
The reduce is on the events array and with every event we try to match it with the element in sequence at the position "size of the accumulator", if it is a match it's added to the accumulator, ortherwise no, etc
here is the mongo playground : https://mongoplayground.net/p/ge4nlFWuLsZ\
The sequence we want to match is in the field "sequence"
The matched elements are in the "matching" field

Sort order in Firestore arrays

I'm trying to understand arrays in Firebase a bit more. Currently, I'm storing maps in arrays, where one of the fields inside the map is a position that I can use in my mobile app to sort the array with on retrieval and show results in the order of position.
The docs on Firebase say:
Arrays are sorted by elements. If elements are equal, the arrays are sorted by length.
For example, [1, 2, 3] < [1, 2, 3, 1] < [2].
And then there's a section describing how maps are sorted as well:
Key ordering is always sorted. For example, if you write {c: "foo", a: "bar", b: "qux"} the map is sorted by key and saved as {a: "foo", b: "bar", c: "qux"}.
Map fields are sorted by key and compared by key-value pairs, first comparing the keys and then the values. If the first key-value pairs are equal, the next key-value pairs are compared, and so on. If two maps start with the same key-value pairs, then map length is considered. For example, the following maps are in ascending order:
{a: "aaa", b: "baz"}
{a: "foo", b: "bar"}
{a: "foo", b: "bar", c: "qux"}
{a: "foo", b: "baz"}
{b: "aaa", c: "baz"}
{c: "aaa"}
But then I tried this in Firestore: I jumbled up the order of the maps in the above example, and stored them in an array:
data= [{"c": "aaa"}, {"a": "aaa", "b": "baz"}, {"a": "foo", "b": "baz"}, {"b": "aaa", "c": "baz"}, {"a": "foo", "b": "bar", "c": "qux"}, {"a": "foo", "b": "bar"}]
And upon inserting into a Firestore document, the array did not get sorted! While the keys themselves do get sorted within a single Map, the elements in the array stay in the same order.
So does sorting in arrays even work when elements are Maps? Here's an example of what I'm storing in Firestore:
{
"car_collection": {
"models": {
data: [
{
"model": "Honda",
"color": "black",
"position": 0
},
{
"model": "Hyundai",
"color": "red",
"position": 1
},
{
"model": "Chevrolet",
"color": "yellow"
"position": 2
}
]
}
}
}
I'm storing an additional field called "position", and the order of maps stays the same on every retrieval. Wondering if I even need to store this field, or data will be sorted in the order that I store it in.
Submitted a ticket to Google to improve the documentation for Array type, and I think it's helpful and accurate as seen through some smoke testing.
https://firebase.google.com/docs/firestore/manage-data/data-types
Copy-pasting the current version here:
An array cannot contain another array value as one of its elements.
Within an array, elements maintain the position assigned to them. When sorting two or more arrays, arrays are ordered based on their element values.
When comparing two arrays, the first elements of each array are compared. If the first elements are equal, then the second elements are compared and so on until a difference is found. If an array runs out of elements to compare but is equal up to that point, then the shorter array is ordered before the longer array.
For example, [1, 2, 3] < [1, 2, 3, 1] < [2]. The array [2] has the greatest first element value. The array [1, 2, 3] has elements equal to the first three elements of [1, 2, 3, 1] but is shorter in length.
So it seems you can safely expect the order of elements to be maintained in Firestore, while understanding the effects of addition/removal as well.
You will have to sort your array before posting it to Firestore.
Arrays are not sorted in RTD nor Firestore objects however are sorted by it's keys.
Or sort the arrays on the client side.

ElasticSearch 5.1 Filtering by Comparing Arrays

I have a keyword array field (say f) and I want to filter documents with an exact array (e.g. filter docs with f = [1, 3, 6] exactly, same order and number of terms).
What is the best way of doing this?
Regards
One way to achieve this is to add a script to the query which would also check the number of elements in the array.
it script would be something like
"filters": [
{
"script": {
"script": "doc['f'].values.length == 3"
}
},
{
"terms": {
"f": [
1,
3,
6
],
"execution": "and"
}
}
]
Hope you get the idea.
I think an even better idea would be to store the array as a string (if there are not many changes to the structure of the graph) and matching the string directly. This would be much faster too.

Resources