merge:
- &LEFT { x: 1, y: 1, r: 1 }
- &BIG { x: 2, y: 2, r: 2 }
- &SMALL { x: 3, y: 3, r: 3}
- # Override
<< : [ *BIG, *LEFT, *SMALL ]
x: 1
label: big/left/small
I get the output:
{
merge:
[
{ x: 1, y: 1, r: 1 },
{ x: 2, y: 2, r: 2 },
{ x: 3, y: 3, r: 3 },
{ x: 1, y: 2, r: 2, label: 'big/left/small' }
]
}
But the results do not meet my expectation, the last one in the merge object I hope it be
{ x: 1, y: 3, r: 3, label: 'big/left/small' }.
How can I do with the YAML syntax ?
You cannot do this with YAML syntax, and your expectations are unfounded on multiple levels.
An anchored element (whether a sequence element or not) doesn't magically disappear when it is used in merge alias or any other alias nor on the basis of it being an anchor
A toplevel mapping key (merge) doesn't magically disappear because its value is a sequence scalar that contains an element with a merge indicator
The Merge Key Language-Independent Type documentation doesn't indicate such a deletion and neither does the YAML specification. The anchors (and aliases) are not normally preserved in the representation in the language you use for loading your YAML, as per the YAML specs. Therefore it is normally not possible to find the anchored elements and delete them after loading.
A generic solution would be to have top another toplevel key default key that "defines" the anchors and work only with the value associated with the merge key:
import ruamel.yaml
yaml_str = """\
default:
- &LEFT { x: 1, y: 1, r: 1 }
- &BIG { x: 2, y: 2, r: 2 }
- &SMALL { x: 3, y: 3, r: 3}
merge:
# Override
<< : [ *BIG, *LEFT, *SMALL ]
x: 1
label: big/left/small
"""
data = ruamel.yaml.load(yaml_str)['merge']
print(data)
gives:
{'x': 1, 'r': 2, 'y': 2, 'label': 'big/left/small'}
(the order of the keys in your output is of course random)
Related
Note: The closer the sum of the prices are to max_price, the better
Initial data:
**max_price** = 11
[
{
id: 1,
price: 5
},
{
id: 2,
price: 6
},
{
id: 3,
price: 6
},
{
id: 4,
price: 1
},
{
id: 5,
price: 3
},
]
For instance, for the first time, we should return
[
{
id: 1,
price: 5
},
{
id: 2,
price: 6
}
]
because the sum of prices of these 2 elements is equal to or less than max_price.
But for the next time, we should return other random elements where their price sum is equal to or less than max_price
[
{
id: 3,
price: 6
},
{
id: 4,
price: 1
},
{
id: 5,
price: 3
}
]
Every time we should return an array with random elements where their sum is equal to or less than max_price.
How can we do that in ruby?
As #Spickerman stated in his comment, this looks like the knapsack problem, and isn't language sensitive at all.
for a Ruby version, I played around a bit, to see how to get the pseudocode working, and I've come up with this as a possible solution for you:
Initialisation of your records:
#prices =
[
{ id: 1, price: 3 },
{ id: 2, price: 6 },
{ id: 3, price: 6 },
{ id: 4, price: 1 },
{ id: 5, price: 5 }
]
# Define value[n, W]
#max_price = 11
#max_items = #prices.size
Defining the Ruby subprocedures, based on that Wiki page, one procedure to create the possibilities, one procedure to read the possibilities and return an index:
# Define function m so that it represents the maximum value we can get under the condition: use first i items, total weight limit is j
def put_recurse(i, j)
if i.negative? || j.negative?
#value[[i, j]] = 0
return
end
put_recurse(i - 1, j) if #value[[i - 1, j]] == -1 # m[i-1, j] has not been calculated, we have to call function m
return unless #prices.count > i
if #prices[i][:price] > j # item cannot fit in the bag
#value[[i, j]] = #value[[i - 1, j]]
else
put_recurse(i - 1, j - #prices[i][:price]) if #value[[i - 1, j - #prices[i][:price]]] == -1 # m[i-1,j-w[i]] has not been calculated, we have to call function m
#value[[i, j]] = [#value[[i - 1, j]], #value[[i - 1, j - #prices[i][:price]]] + #prices[i][:price]].max
end
end
def get_recurse(i, j)
return if i.negative?
if #value[[i, j]] > #value[[i - 1, j]]
#ret << i
get_recurse(i - 1, j - #prices[i][:price])
else
get_recurse(i - 1, j)
end
end
procedure to run the previously defined procedures in a nice orderly fashion:
def knapsack(items, weights)
# Initialize all value[i, j] = -1
#value = {}
#value.default = -1
#ret = []
# recurse through results
put_recurse(items, weights)
get_recurse(items, weights)
#prices.values_at(*#ret).sort_by { |x| x[:id] }
end
Running the code to get your results:
knapsack(#max_items, #max_price)
Consider the following file YAML file:
A:
&B
- x: 1
y: 2
- x: 10
y: 20
C:
<<: *B
which is read into python via:
from ruamel.yaml import YAML
filename = 'data/configs/debug_config.yml'
with open(filename) as f:
c = YAML(typ='safe').load(f)
print(c)
yielding:
{'A': [{'x': 1, 'y': 2}, {'x': 10, 'y': 20}], 'C': {'x': 1, 'y': 2}}
It is seen that the anchor B only includes the first element of the sequence. Why? I would like an anchor that would include the entire sequence, such that the values of A and C in the python dictionary are identical. How can this be done?
The anchor B includes all elements from A, but you are merging them with the merge key << (source):
If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier in the sequence override keys specified in later mapping nodes.
So,the first item from A overrides the second one.
Remove <<: and C will be the same dictionary as A:
A: &B
- x: 1
y: 2
- x: 10
y: 20
C: *B
Yields:
{
"A": [
{
"y": 2,
"x": 1
},
{
"y": 20,
"x": 10
}
],
"C": [
{
"y": 2,
"x": 1
},
{
"y": 20,
"x": 10
}
]
}
You can check the merge order with this example:
A: &B
- x: 1
y: 2
- x: 10
new: 333
C:
<<: *B
Yields:
{
"A": [
{
"y": 2,
"x": 1
},
{
"x": 10,
"new": 333
}
],
"C": {
"y": 2,
"x": 1,
"new": 333
}
}
As #Hadyniak already indicated you are incorrectly using the merge
keys. Since the alias *B is expanded in the
composer step, before the interpretation of the << merge key in the
constructor step, the latter actually receives a list of dicts, which are
combined with the values of keys of earlier occuring dicts taking precedence. If
the order of processing had happened to be different you would get an error, and
IMO the merge key documentation doesn't explicitly specify that aliases should
be expanded first.
Hadyniak's solution however will have you end
up with c['A'] and c['C'] being the same dictionary and that might not be what you want:
import ruamel.yaml
yaml_str = """\
A:
&B
- x: 1
y: 2
- x: 10
y: 20
C:
*B
"""
yaml = ruamel.yaml.YAML()
c = yaml.load(yaml_str)
print(c['A'] is c['C'], c['A'] == c['C'])
c['A'][0]['x'] = 42
print('x:', c['C'][0]['x'])
which gives:
True True
x: 42
If that is not what you want, you can still use merge keys, but on the dictionaries that are the elements of your sequence:
import ruamel.yaml
yaml_str = """\
A:
- &B1
x: 1
y: 2
- &B2
x: 10
y: 20
C:
- <<: *B1
- <<: *B2
"""
yaml = ruamel.yaml.YAML()
c = yaml.load(yaml_str)
print(c['A'] is c['C'], c['A'] == c['C'])
c['A'][0]['x'] = 42
print('x:', c['C'][0]['x'])
which gives:
False True
x: 1
Alternatively you can tell the composer part of ruamel.yaml to expand the aliases instead of using a reference:
import copy
yaml_str = """\
A:
&B
- x: 1
y: 2
- x: 10
y: 20
C:
*B
"""
yaml = ruamel.yaml.YAML()
yaml.composer.return_alias = lambda s: copy.deepcopy(s)
c = yaml.load(yaml_str)
print(c['A'] is c['C'], c['A'] == c['C'])
c['A'][0]['x'] = 42
print('x:', c['C'][0]['x'])
which gives:
False True
x: 1
The above will only work in ruamel.yaml>0.17.2, for older versions you'll need to copy and modify the compose_node method:
import copy
yaml_str = """\
A:
&B
- x: 1
y: 2
- x: 10
y: 20
C:
*B
"""
class MyComposer(ruamel.yaml.composer.Composer):
def compose_node(self, parent, index):
# type: (Any, Any) -> Any
if self.parser.check_event(ruamel.yaml.events.AliasEvent):
event = self.parser.get_event()
alias = event.anchor
if alias not in self.anchors:
raise ComposerError(
None,
None,
'found undefined alias {alias!r}'.format(alias=alias),
event.start_mark,
)
return copy.deepcopy(self.anchors[alias])
event = self.parser.peek_event()
anchor = event.anchor
if anchor is not None: # have an anchor
if anchor in self.anchors:
ws = (
'\nfound duplicate anchor {!r}\nfirst occurrence {}\nsecond occurrence '
'{}'.format((anchor), self.anchors[anchor].start_mark, event.start_mark)
)
warnings.warn(ws, ruamel.yaml.error.ReusedAnchorWarning)
self.resolver.descend_resolver(parent, index)
if self.parser.check_event(ruamel.yaml.events.ScalarEvent):
node = self.compose_scalar_node(anchor)
elif self.parser.check_event(ruamel.yaml.events.SequenceStartEvent):
node = self.compose_sequence_node(anchor)
elif self.parser.check_event(ruamel.yaml.events.MappingStartEvent):
node = self.compose_mapping_node(anchor)
self.resolver.ascend_resolver()
return node
yaml = ruamel.yaml.YAML()
yaml.Composer = MyComposer
c = yaml.load(yaml_str)
print(c['A'] is c['C'], c['A'] == c['C'])
c['A'][0]['x'] = 42
print('x:', c['C'][0]['x'])
which also gives:
False True
x: 1
I am trying to write a program that simplifies mathematical expressions.
I have already written a parser that converts a string to a binary tree.
For example (1+2)*x will become
*
/ \
+ x
/ \
1 2
The idea I had of simplifying such trees is as follows:
You store a set of trees and their simplified version
For example
* +
/ \ / \
a + and * *
/ \ / \ / \
b c a b a c
(Where a,b,c can be any subtree)
Then, If I find a subtree that matches one of the stored trees, I will
replace it with its simplified version.
If necessary I will repeat the process until the tree is fully simplified.
The problem with this approach is that it can't "combine like terms" in some cases.
For example, if I try to store the tree:
+ *
/ \ and / \
x x 2 x
Then when I try to simplify the expression x+y+x, with the following tree:
+
/ \
x +
/ \
y x
It will not be simplified to 2x+y, because the subtree
+
/ \
x x
Is not contained in the tree, thus the tree will not be simplified.
I tried writing an explicit algorithm that combine like terms but there are too many
cases to consider.
Can anyone please help me find a solution to this problem?
Here is one of the basic ideas which is used in computer algebra systems.
For operators like Plus (+) and Times (*) you can define attributes like Flat (associativity) and Orderless (commutativity). Also don't define Plus and Times as "binary" operators but as "multiple-argument" operators.
So an input like:
Plus(x,Plus(y,x))
in the first step can be transformed (flattened) because of the Flat attribute to
Plus(x,y,x)
in the next step it can be transformed (sorted) because of the Orderless attribute to
Plus(x,x,y)
In your "evaluation" step you can now go through the arguments and "simplify" the expression to:
Plus(Times(2,x),y)
This approach has the advantage that expressions which are "structural equal" are stored in the same "canonical form" and could for example easier compared for "object equality" in the used programming language.
We may consider polynomials
we get the '+'-reducer and '*'-reducer for two polynomes in X
Now in the tree, instead of considering either a scalar or x as node, we may consider an "irreducible" polynomial.
Then we apply the '*'-reducer if the node operator is * or '+'-reducer otherwise which both transform two irreducibles polynome as an new irreducible one.
e.g where P_a, P_b two polynomes and
P_a = {
x0: 1 // term of degree 0 idem 1
x1: 2 // 2x
x3: 4 // 4x^3
}
and P_b = {x1: 3}
we get for sum: P_a + P_b = {x0: 1, x1: 5, x3: 4}
(so tree ['+', P_a, P_b] simplifies as {x: 0, x1: 5, x3: 4})
we get for multiplication: P_a * P_b = {x1: 3, x2: 6, x3: 12}
At the end of the day, we get an irreducible polynome in X.
We can write back that polynome as a binary tree (which is thus a simplified tree):
for each monome (in X^i), write its associated binary tree (which only contains * operator)
e.g: 5x^3 => ['*', ['*', ['*', x, x], x], 5]
then sum them
e.g: 1 + x + x^2 => ['+', 1, ['*', x, 1], ['*', x, x]
Same idea (idem implementing '+'-reducer/'*'-reducer) can be applied with an expression having polynomes in X, Y or Z, or whatever (so in your case, x, y)
below an example of implementation (you may uncomment and pass the tests using nodejs)
// a, b are polynomes of form {monomialKey: scalar, monomialKey2, scalar2, ...}
// a monomial key is e.g x1y2z2
const add = (a, b) => {
const out = Object.assign({}, a)
Object.entries(b).forEach(([monomialKey, scalar]) => {
out[monomialKey] = (out[monomialKey] || 0) + scalar
if (out[monomialKey] === 0) {
delete out[monomialKey]
}
})
return out
}
// transforms x1y2z2 to {x: 1, y: 2, z: 2}
const parseKey = s => s.match(/[a-z]+\d+/g).reduce((o, kv) => {
const [,varname,deg] = kv.match(/([a-z]+)(\d+)/)
o[varname] = parseInt(deg)
return o
}, {})
const writeKey = o => Object.entries(o).reduce((s, [varname, deg]) => s + varname+deg, '')
// simplify monomial, e.g x1y3*x1 => x2y3
const timesMonomialKey = (iA, iB) => {
const a = parseKey(iA)
const b = parseKey(iB)
const out = {}
;[a,b].forEach(x => Object.entries(x).forEach(([varname, deg]) => {
if (deg === 0) return
out[varname] = (out[varname] || 0) + deg
}))
if (Object.keys(out).length === 0) return writeKey({ x: 0 })
return writeKey(out)
}
// a, b both polynomes
const times = (a, b) => {
const out = {}
Object.entries(a).forEach(([monimalKeyA, sA]) => {
Object.entries(b).forEach(([monimalKeyB, sB]) => {
const key = timesMonomialKey(monimalKeyA, monimalKeyB)
out[key] = (out[key] || 0) + sA * sB
if (out[key] === 0) {
delete out[key]
}
})
})
return out
}
const reduceTree = t => { // of the form [operator, left, right] or val
if (!Array.isArray(t)) {
return typeof(t) === 'string'
? { [writeKey({ [t]: 1 })]: 1 } // x => {x1: 1}
: { [writeKey({ x: 0 })]: t } // 5 => {x0: 5}
}
const [op, leftTree, rightTree] = t
const left = reduceTree(leftTree)
const right = reduceTree(rightTree)
return op === '+' ? add(left, right) : times(left, right)
}
const writePolynomial = o => {
const writeMonomial = ([key, s]) => {
const a = parseKey(key)
const factors = Object.entries(a).flatMap(([varname, deg]) => {
return Array.from({length: deg}).fill(varname)
}).concat(s !== 1 ? s : [])
return factors.reduce((t, next) => ['*', t, next])
}
if (Object.keys(o).length === 0) return 0
return Object.entries(o).map(writeMonomial).reduce((t, next) => ['+', t, next])
}
console.log(writePolynomial(reduceTree(['+', ['+', 'x', 'y'], 'x'])))
//const assert = require('assert')
//assert.deepEqual(parseKey('x0y2z3'), { x: 0, y: 2, z: 3 })
//assert.deepEqual(writeKey({ x: 0, y: 2, z: 3 }), 'x0y2z3')
//assert.deepEqual(timesMonomialKey('x1y2', 'x3z1'), 'x4y2z1')
//assert.deepEqual(timesMonomialKey('x0y0', 'z0'), 'x0')
//assert.deepEqual(timesMonomialKey('x0y0', 'z0x1'), 'x1')
//assert.deepEqual(add({x0: 3, x1: 2}, {x0: 4, x3: 5}), {x0: 7, x1: 2, x3: 5})
//assert.deepEqual(add({x0: 3, y1: 2}, {x0: 4, y2: 5}), {x0: 7, y1: 2, y2: 5})
//assert.deepEqual(add({x0: 1}, {x0: -1}), {})
//assert.deepEqual(times({x0: 3, x1: 2}, {x0: 4, x1: 5}), {x0: 12, x1: 23, x2: 10})
//assert.deepEqual(times(
// {x1y0: 3, x1y1: 2},
// {x1y0: 4, x1y1: 5}),
// {x2: 12, x2y1: 23, x2y2: 10}
//)
//assert.deepEqual(reduceTree('x'), {x1: 1})
//assert.deepEqual(reduceTree(['*', 2, 'x']), {x1: 2})
//assert.deepEqual(reduceTree(['+', 2, 'x']), {x0: 2, x1: 1})
//assert.deepEqual(reduceTree(['+', 'x', ['+', 'y', 'x']]), {x1: 2, y1: 1})
//assert.deepEqual(writePolynomial({ x1y1:1, x1y2: 2}), ['+', ['*', 'x', 'y'], ['*', ['*', ['*', 'x', 'y'], 'y'], 2]])
//assert.deepEqual(writePolynomial(reduceTree(['*', ['*', 'x', 'y'], 0])), 0)
//assert.deepEqual(writePolynomial(reduceTree(['+', ['*', ['*', 'x', 'y'], 0], 2])), 2)
//
//// finally your example :)
//assert.deepEqual(writePolynomial(reduceTree(['+', ['+', 'x', 'y'], 'x'])), ['+', ['*', 'x', 2], 'y'])
I have an array of hashes that have numbers:
[
{
y: 316.28,
label: "Kaimur",
color: "Light_Green"
},
{
y: 323.63,
label: "Banka",
color: "Light_Green"
},
{
y: 327.85,
label: "Gaya",
color: "Light_Green"
},
{
y: 346.11,
label: "EastChamparan",
color: "Light_Green"
},
{
y: 358.38,
label: "Nalanda",
color: "Light_Green"
},
{
y: 363.13,
label: "Madhubani",
color: "Light_Green"
}
]
Here my first number is 316.28 and last number is 363.13. I want to created ranges from this array like 300 to 400. This is an example using the first and the last element of the array.
I want to make it like 300 to 400 or 100 to 200 or 10 to 20.
If my number is 316.28, I want to return a value 300 and if my value is 363.13, then it should return 400.
How can I do that?
I want to round my values if my list of array has three number 2 numbers or 4 numbers such as 12.5, 123.45, or 3900.56. These can be the number and all of my array can have these kind of numbers. If I have to round each number after finding a length, that becomes a nightmare. I need a function which can do the trick.
Use Float#round with a negative argument:
316.28.round(-2)
#⇒ 300
363.13.round(-2)
#⇒ 400
input = _
#⇒ [{:y=>316.28, :label=>"Kaimur", :color=>"Light_Green"},
# {:y=>323.63, :label=>"Banka", :color=>"Light_Green"},
# {:y=>327.85, :label=>"Gaya", :color=>"Light_Green"},
# {:y=>346.11, :label=>"EastChamparan", :color=>"Light_Green"},
# {:y=>358.38, :label=>"Nalanda", :color=>"Light_Green"},
# {:y=>363.13, :label=>"Madhubani", :color=>"Light_Green"}]
ys = input.map { |e| e[:y] }
#⇒ [316.28, 323.63, 327.85, 346.11, 358.38, 363.13]
Range.new *[ys.min, ys.max].map { |e| e.round(-e.round.to_s.length+1) }
#⇒ 300..400
Suppose I have a lot of documents like {'a' : x , 'b' : y}.
Suppose x and y are integers.
How can I do something like find().sort({'a'/'b'}) ?
Since this question was asked in 2011, MongoDB has released the aggregation framework. It lets you sort by the combinations of fields, and you don't need to store (denormalize) any extra fields. Here's how it's done:
db.collection.aggregate([
{
$match: {
// Optional criteria to select only some documents to process, such as...
deleted: null
}
},
{
$project: {
// Need to prefix fields with '$'
ratio: { $divide: [ "$a", "$b" ] },
}
},
{
$sort: { ratio: -1 },
}
]);
That's it. We use the $divide operator of the aggregation framework.
You can add third field, result of a/b and sort by it.
You document will looks like:
{'a' : x , 'b' : y, c : z} // z = x/y
And you will sort by 'c':
find().sort({c : 1})
I don't believe this is possible, as you also can't run queries that compare 2 fields (without using $where to specify a javascript function which would be slow). Instead, I suspect you need to also store the ratio separately within the document and then sort on that new field.
db.collection.aggregate([
{ $addFields: { newField: { $divide: [ "$a", "$b" ] } } }, // Prefix fields with '$'
{ $sort: { newField: -1 } }
]);
Just like Bugai13 said, you need a need a third property in your collection in order to perform the sort. You can add the ratio property with a call to mapReduce (as follows), but this won't be terribly fast on large collections - and will lock up your database while it is running. You really should manually keep the ratio property up to date - it should't be very hard.
db.data.insert({a: 1, b: 1});
db.data.insert({a: 2, b: 2});
db.data.insert({a: 3, b: 3});
db.data.insert({a: 1, b: 4});
db.data.insert({a: 2, b: 1});
db.data.insert({a: 3, b: 2});
db.data.insert({a: 1, b: 3});
db.data.insert({a: 2, b: 4});
db.data.insert({a: 3, b: 1});
db.data.insert({a: 1, b: 2});
db.data.insert({a: 2, b: 3});
db.data.insert({a: 3, b: 4});
db.data.mapReduce(
function(){
emit(this._id, this);
},
function(k, vs){
v = vs[0];
v.c = v.a / v.b;
return v;
},
{out : 'data'}
);
db.data.find().sort({c:1});