Three vehicle types are Car, Motorcycle and Bicycle. Three statuses are Available, Reserved and Sold.
I want to print the information of all vehicles that are not Car and are not Sold, i.e. sold cars should not be printed. In other words, print information of everything that is Motorcycle or Bicycle, with any status among Available, Reserved and Sold. If it is a Car, still print as long as it is Available or Reserved.
The vehicles are:
Car - Sold
Car - Available
Car - Reserved
Bicycle - Sold
Bicycle - Available
Bicycle - Reserved
Motorcycle - Sold
Motorcycle - Available
Motorcycle - Reserved
I expect the following code to print everything except number 1 (Car - Sold)
My code:
for _, v := range results {
if v.Type != "Car" && v.Status != "Sold" { // && does not work but || works
resp = append(resp, &VehicleInfo {
ID: v.Id,
Brand: v.Brand,
Type: v.Type,
Status: v.Sold,
})
}
}
fmt.Println(resp)
When I use AND (&&), Println result is very strange, it outputs 5, 6, 8, 9. However, when I switch to OR (||), it prints exactly what I want, which is everything except 1 (sold car), which is a list of all Motorcycle (any status), all Bicycle (any status) and all Car that is either Available or Reserved.
What is the problem here? I thought using AND (&&) was the right answer, but it is not.
Your problem statement is... unclear. The statement
I want to print the information of all vehicles that are not Car and are not Sold,...
But the remainder of the problem statement:
... i.e. sold cars should not be printed. In other words, print information of everything that is Motorcycle or Bicycle, with any status among Available, Reserved and Sold. If it is a Car, still print as long as it is Available or Reserved.
Indicates that you want to filter out (exclude) cars that have been sold.
The easiest way is something like this:
for _, v := range results {
isSoldCar := v.Type == "Car" && v.Status == "Sold"
if isSoldCar {
continue
}
resp = append(resp, &VehicleInfo {
ID: v.Id,
Brand: v.Brand,
Type: v.Type,
Status: v.Sold,
})
}
or this:
for _, v := range results {
isSoldCar := v.Type == "Car" && v.Status == "Sold"
if !isSoldCar {
resp = append(resp, &VehicleInfo {
ID: v.Id,
Brand: v.Brand,
Type: v.Type,
Status: v.Sold,
})
}
}
Related
I am building an integration between Shopify and our ERP via the admin API using GraphQL. All is working well except when I try and get the exact prices for an order.
In the documentation discountedTotalSet should be 'The total line price after discounts are applied' but I am finding it returns the full price - see examples below.
Can anyone give me guidance on how to get the API to show the same prices that are on the order? I need this to match exactly line by line. This is the query I am using for the order:
{
node(id: "gid://shopify/Order/4866288156908") {
id
...on Order {
name
lineItems (first: 50) {
edges {
node {
id
quantity
sku
discountedTotalSet {
shopMoney {
currencyCode
amount
}
}
}
}
}
}
}
}
And this is the result, note amount says 599.00 but that is not correct, see screenshot for the same order from the UI.
{
"data": {
"node": {
"id": "gid://shopify/Order/4866288156908",
"name": "AK-1003",
"lineItems": {
"edges": [
{
"node": {
"id": "gid://shopify/LineItem/12356850286828",
"quantity": 1,
"sku": "AK-A1081",
"discountedTotalSet": {
"shopMoney": {
"currencyCode": "AUD",
"amount": "599.0"
}
}
}
}
]
}
}
},
Shopify UI screenshotemphasized text
discountedTotalSet gives you the amount after discounts applied to that particular line. In your example you're applying a discount to the whole order. There is no field, in the lineItem object that will give you the expected value for that line.
So you have to distribute the whole discount to each single line.
I had the exact same problem and I had to implement this solution in python, I hope it helps:
from decimal import Decimal
def split_discounts(money, n):
quotient = Decimal(round(((money * Decimal(100)) // n) / Decimal(100), 2))
remainder = int(money * 100 % n)
q1 = Decimal(round(quotient + Decimal(0.01), 2)) # quotient + 0.01
result = [q1] * remainder + [quotient] * (n - remainder)
return result # returns an array of discounted amounts
def retrieve_shop_money(obj):
return Decimal(obj['shopMoney']['amount']) if obj and obj['shopMoney'] and obj['shopMoney']['amount'] else Decimal(
0) # this is just to retrieve the inner shopMoney field
def get_line_price(order_node):
discount = retrieve_shop_money(order_node["cartDiscountAmountSet"])
non_free_lines = len([1 for item in order_node["lineItems"]["edges"] if
retrieve_shop_money(item["node"]["discountedTotalSet"]) > 0])
if non_free_lines > 0:
discounts = split_discounts(discount, non_free_lines)
else:
discounts = 0 # this was an edge case for me, that you might not consider
discounted = 0
for item in order_node["lineItems"]["edges"]:
gross = retrieve_shop_money(item["node"]["originalTotalSet"]) # THIS IS THE VALUE WITHOUT DISCOUNTS
net = retrieve_shop_money(item["node"]["discountedTotalSet"])
if net > 0: # exluding free gifts
net = net - discounts[discounted] # THIS IS THE VALUE YOU'RE LOOKING FOR
discounted = discounted + 1
So first I retrieve if the whole order was free. This was an edge case that was giving me some issues. In that case I just know that 0 is the answer I want.
Otherwise with the method split_discounts I calculate each single disount to be applied to the lines. Discounts can be different because if you discount $1 out of 3 items is going to be [0.33,0.33,0.34]. So the result is an array. Then I just loop through the lines and apply the discount if discountedTotalSet is >0.
Thinking about it, you might also want to be sure that the discount is greater than the value of the line. But that is an edge case that I never encouted, but depends on the kind of discounts you have.
I have a structure where I create and store books as follows.
type Book struct {
Authors [] string
ISBN string
Countries [] string
Category string
}
type Books []Book
Then I define the values as follows.
test123 := Books{}
test123 = append(test123, Book{
Authors: []string{"Alice", "John", "Bob"},
Countries: []string{"CA", "US", "DE"},
ISBN: "1234567",
Category: "sci-fi",
})
test123 = append(test123, Book{
Countries: []string{"UK"},
Category: "comic book",
ISBN: "0001298",
})
test123 = append(test123, Book{
Authors: []string{"Alice", "Bob"},
Countries: []string{"UK"},
Category: "comic book",
ISBN: "0001298",
})
test123 = append(test123, Book{
ISBN: []string{"FR", "AU"},
Category: "sci-fi",
})
Not every Book structure has all defined elements. So some Book structure elements may not have an ISBN and/or a Countries value.
How can I search in such a structure? For example, I have DE as the Country value and then give Alice as the Author value. How can I write a code that can find and retrieve relevant elements when given the value of both Alice and DE?
UPDATE
#Cerise Limón and #gonutz I haven't asked the question exactly, I'm sorry. This code only works for DE and Alice values, not for other values. For other combinations, I have to write blocks of code that consist of if and else. In fact, regardless of the values I will give, I want to make the algorithm that finds values when I search in any slice structure. Is it possible?
Sample Scenario - 1:
I have DE value for Author for Alice Countries. I want to find the "slices" that contain these values in the Books struct.
Sample Scenario - 2:
I have 123456 for ISBN, FR for Countries, Bob for Authors. I want to find the "slices" that contain these values in the Books struct.
In other words, the type and number of values I will give as input can be different.
Just code up what you want to be able to search for, just if and for like this:
for _, b := range books {
hasDE := false
for _, c := range b.Countries {
if c == "DE" {
hasDE = true
}
}
isAlice := false
for _, a := range b.Authors {
if a == "Alice" {
isAlice = true
}
}
if hasDE && isAlice {
fmt.Println(b)
}
}
Google Cloud datastore does not allow to make SQL style search queries like
SELECT * FROM Person WHERE Name LIKE "Rob*"
that would return Rob, Robert, Roberto, Roberta, Roby and so on.
GCP datastore only allows to filter on fields using operators: >, >=, <, <= with rules like:
any upper case letter is smaller than any lower case letter
a < b < c < ... < z
With these rules, the query
query := datastore.NewQuery("Person").Filter("Name >= ", "Rob").Order("Name")
would not only return all Person whose name starts with Rob, but also all Person whose name is greater than Rob (Ruben, Sarah, Zoe) and all Person whose names starts with a lower case letter.
The present post is a hack that I found in Go to emulate a SQL style search query.
Specifically for prefix matches you can use multiple inequality filters on the same property. I.e. from https://cloud.google.com/datastore/docs/concepts/queries "If a query has multiple inequality filters on a given property, an entity will match the query only if at least one of its individual values for the property satisfies all of the filters."
The example on that page is SELECT * FROM Task WHERE tag > 'learn' AND tag < 'math'. Or for your case query := datastore.NewQuery("Person").Filter("Name >= ", "Rob").Filter("Name <= Rob~").Order("Name")
The following solution addresses the issue programmatically. It is coded in go but i believe it could be easily adapted to any language.
I'll first produce the whole snippet and then I shall break it down.
func (store *PersonStore) Search(name string) ([]Person, error) {
context := context.Background()
persons := make([]*Person, 0)
query := datastore.NewQuery("Person").Filter("Name >= ", strings.ToLower(name)).Order("Name")
keys, err := store.client.GetAll(context, query, &persons)
if err != nil {
return nil, fmt.Errorf("unable to search for persons w/ names containing %s - %v", name, err)
}
filteredPersons := make([]*Perons, 0)
for i, p := range persons {
p.ID = keys[i].ID
if !strings.Contains(p.Name, strings.ToLower(name)) {
break
} else {
filteredPersons = append(filteredPersons, p)
}
}
}
1 - Lower case assumption
In order for this code to work we first need to make a very strong assumption that all names are in lower case. If, for some reason, you cannot make this assumption, you can still partially use this code, but it will be less efficient.
2 - Query the datastore
The first part of the code is dedicated to fetching the datastore for Persons whose name is matching the desired pattern.
context := context.Background()
persons := make([]*Person, 0)
query := datastore.NewQuery("Person").Filter("Name >= ", strings.ToLower(name)).Order("Name")
keys, err := store.client.GetAll(context, query, &persons)
if err != nil {
return nil, fmt.Errorf("unable to search for persons w/ names containing %s - %v", name, err)
}
We make sure we use strings.ToLower(name) to make sure we won't fetch ALL Persons. Ideally name should be also trimmed, but this is usually done in the front-end, so we ommitted this here.
Once again, this is base on the assumption that all Names are in lower case. If you cannot assume this, you can always use
query := datastore.NewQuery("Person").Filter("Name >= ", name).Order("Name")
You'll just simply get an initial list with (possibly a lot) more Persons.
Finally, we order our fetched list with .Order("Name") as this is going to be the base point of the second part of the code.
3 - Filter on Names
Up to here it was a simple GetAll piece of code. We still need to insert the keys into the Person structures. We need to find a way to optimize that. For this we can rest on the fact that persons and keys list are in exact length and order. So the upcoming for loop starts exactly like a regular insert Key into structure bit.
for i, p := range persons {
p.ID = keys[i].ID
The next bit is where the optimization is: since we know that the Persons are ordered by Name we are certain that, as soon as strings.Contains(p.Name, strings.ToLower(name)) is not true we have selected all Persons whose Name matches our criteria, that is, as soon as p.Name doesn't start with Rob anymore but with Roc or Rod or anything lexicographically greater than this.
if !strings.Contains(p.Name, strings.ToLower(name)) {
break
We can then escape our loop using a break instruction, hopefuly having only parsed the first few elements of persons list that matche our criteria. These fall into the else statement:
} else {
filteredPersons = append(filteredPersons, p)
}
4 - Filter on Names without the lower case assumption
As I said earlier, if you cannot assume all names are in lower case, you can still use this code, but it will not be optimized, because you will mandatorily have to scan the full persons list returned by the query.
The code should look liske this
filteredPersons := make([]*Perons, 0)
for i, p := range persons {
p.ID = keys[i].ID
if strings.Contains(p.Name, strings.ToLower(name)) {
filteredPersons = append(filteredPersons, p)
}
}
Title: How do I dynamically name a collection?
Pseudo-code: collect(n) AS :Label
The primary purpose of this is for easy reading of the properties in the API Server (node application).
Verbose example:
MATCH (user:User)--(n)
WHERE n:Movie OR n:Actor
RETURN user,
CASE
WHEN n:Movie THEN "movies"
WHEN n:Actor THEN "actors"
END as type, collect(n) as :type
Expected output in JSON:
[{
"user": {
....
},
"movies": [
{
"_id": 1987,
"labels": [
"Movie"
],
"properties": {
....
}
}
],
"actors:" [ .... ]
}]
The closest I've gotten is:
[{
"user": {
....
},
"type": "movies",
"collect(n)": [
{
"_id": 1987,
"labels": [
"Movie"
],
"properties": {
....
}
}
]
}]
The goal is to be able to read the JSON result with ease like so:
neo4j.cypher.query(statement, function(err, results) {
for result of results
var user = result.user
var movies = result.movies
}
Edit:
I apologize for any confusion in my inability to correctly name database semantics.
I'm wondering if it's enough just to output the user and their lists of both actors and movies, rather than trying to do a more complicated means of matching and combining both.
MATCH (user:User)
OPTIONAL MATCH (user)--(m:Movie)
OPTIONAL MATCH (user)--(a:Actor)
RETURN user, COLLECT(m) as movies, COLLECT(a) as actors
This query should return each User and his/her related movies and actors (in separate collections):
MATCH (user:User)--(n)
WHERE n:Movie OR n:Actor
RETURN user,
REDUCE(s = {movies:[], actors:[]}, x IN COLLECT(n) |
CASE WHEN x:Movie
THEN {movies: s.movies + x, actors: s.actors}
ELSE {movies: s.movies, actors: s.actors + x}
END) AS types;
As far as a dynamic solution to your question, one that will work with any node connected to your user, there are a few options, but I don't believe you can get the column names to be dynamic like this, or even the names of the collections returned, though we can associate them with the type.
MATCH (user:User)--(n)
WITH user, LABELS(n) as type, COLLECT(n) as nodes
WITH user, {type:type, nodes:nodes} as connectedNodes
RETURN user, COLLECT(connectedNodes) as connectedNodes
Or, if you prefer working with multiple rows, one row each per node type:
MATCH (user:User)--(n)
WITH user, LABELS(n) as type, COLLECT(n) as collection
RETURN user, {type:type, data:collection} as connectedNodes
Note that LABELS(n) returns a list of labels, since nodes can be multi-labeled. If you are guaranteed that every interested node has exactly one label, then you can use the first element of the list rather than the list itself. Just use LABELS(n)[0] instead.
You can dynamically sort nodes by label, and then convert to the map using the apoc library:
WITH ['Actor','Movie'] as LBS
// What are the nodes we need:
MATCH (U:User)--(N) WHERE size(filter(l in labels(N) WHERE l in LBS))>0
WITH U, LBS, N, labels(N) as nls
UNWIND nls as nl
// Combine the nodes on their labels:
WITH U, LBS, N, nl WHERE nl in LBS
WITH U, nl, collect(N) as RELS
WITH U, collect( [nl, RELS] ) as pairs
// Convert pairs "label - values" to the map:
CALL apoc.map.fromPairs(pairs) YIELD value
RETURN U as user, value
I am well and truly stuck for some reason. I have a bunch of XML which via linq I have adjusted to fit into my DTO objects, this works fine, but I need an additional filter that only returns the Room Types that have rooms that have full availability for a period.
Now my original query to setup the DTO Works fine, but I would like to add something that only returns the rooms that have rates available for the entire periods, so say you want to book 10 days, you should only get the room types back that have the full 10 days available. My original query is the following:
var items = (
from rt in data.Descendants("RoomType")
select new RoomType
{
name = rt.Descendants("RoomDescription").Descendants("Text").SingleOrDefault().Value,
rooms = (
from r in rt.Descendants("Room")
select new Room
{
Name = r.Attribute("id").Value,
rates = (
from rr in r.Descendants("RoomRate")
where DateTime.Parse(rr.Attribute("EffectiveDate").Value) >= startDate
where DateTime.Parse(rr.Attribute("EffectiveDate").Value) <= endDate
select new RoomRate
{
EffectiveDate = DateTime.Parse(rr.Attribute("EffectiveDate").Value)
})
})
});
if it is at all possible to have the restriction in this query that would be amazing, but I couldn't see how to do it.
When I tried to create another query off the back of this one I didn't know how I could query the count of Rooms.RoomRates from the RoomType object to return. I tried
var result = items.Where(i => i.rooms.Where(r => r.rates.Count() == 10));
but that gives me an exception where it can't convert IEnumerable to bool, .Any() compiles but returns everything (as probably expected).
Does anyone know what I am doing wrong here?
EDIT: ** this is how it is pulling the data out at the moment
Room Type: Single
Room 1 (Days Available 10)
Room 2 (Days Available 10)
Room Type: Twin
Room 3 (Days Available 10)
Room 4 (Days Available 4)
what I am trying to do is exclude Room 4 from returning as it doesn't meet the days criteria
so what I should get back is:
Room Type: Single
Room 1 (Days Available 10)
Room 2 (Days Available 10)
Room Type: Twin
Room 3 (Days Available 10)
If you only want Rooms, you can just flatten the collection, then filter it:
IEnumerable<Room> rooms = items.SelectMany(i => i.Rooms)
.Where(r => r.rates.Count() == 10)
If you want RoomTypes, you'll need to create new RoomType objects with filtered Rooms:
var types = items.Select(i =>
new RoomType {
name = i.name,
rooms = i.rooms.Where(r => r.rates.Count() == 10)
}
);