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)
}
}
Related
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,
})
}
}
I would like to sort a list of objects by a Boolean property and then a String property.
The String should be compared case-insensitively, and using natural sort order as defined here
An example could be sorting persons by alive and then name:
class Person(
public val name: String,
public val alive : Boolean
)
I want the name sorting to look like this:
"1 Adam"
"13 Adam"
"20 adam"
"100 Adam"
"Adam 0"
"Adam 1"
"adam 10"
"Adam 20"
"Adam 100"
"Brian"
"George"
I have experimented with sorting a list of strings using the naturalOrder Comparable, and it does look promising although it seems to be case sensitive:
val personsStr = setOf("1 Adam", "21 Adam", "13 Adam", "Adam 1", "adam 1", "Adam 0", "Adam 20", "Adam 11", "Bryan", "George")
val personStrSorted = personsStr.sortedWith(naturalOrder())
What I am looking for is something like:
persons.sortedWith( compareBy<Person>{ it.alive }.thenBy{ it.name.toLowerCase() /*and done using natural sorting*/ } )
Question:
In which way(s) can the desired sorting be achieved?
Somewhat hacky solution: Split your set into 2 sets by alive and sort both sets by name (this results in a natural order)::
val personsStr = setOf( Person("1 Adam", true ), Person("21 Adam", false), Person("13 Adam", true), Person( "Bryan", true) )
val personsAlive = personsStr.filter{it.alive}.sortedBy{it.name}
val personsNotAlive = personsStr.filter{!it.alive}.sortedBy{it.name}
then you can merge them:
val result = setOf(personsAlive, personsNotAlive)
I believe you will have to implement your notion of "natural ordering" manually.
The "natural order" in Kotlin is simply defined as the order by which Comparable<T> implementations are sorted when no special comparator is provided (doc link):
First, there is natural order. It is defined for inheritors of the Comparable interface. Natural order is used for sorting them when no other order is specified.
Using naturalOrder() should give you the usual order defined by the compareTo operator for any implementor of Comparable. This means that for string, the natural order is in fact the alphabetical order.
If you know that your names are always in the form <name>, <num> <name> or <name> <num>, you should be able to implement a comparator by actually parsing the number.
One way to do it could be something like this:
fun main() {
val personsStr = setOf("1 Adam", "21 Adam", "100 Adam", "Adam 1", "adam 1",
"Adam 3", "Adam 20", "Adam 11", "Bryan", "George", "bob" )
val persons = personsStr.map { Person(name = it, alive = kotlin.random.Random.nextBoolean()) }
val personsSorted = persons.sortedWith(compareBy({ it.alive }, { it.name.parseName() }))
println(personsSorted.joinToString("\n"))
}
data class Person(
val name: String,
val alive: Boolean
)
data class ComplexName(
val leadingNum: Int?,
val shortName: String,
val trailingNum: Int?
) : Comparable<ComplexName> {
override operator fun compareTo(other: ComplexName) = compareValuesBy(this, other, comparator, { it })
companion object {
private val comparator = compareBy<ComplexName>(
{ it.leadingNum ?: Int.MAX_VALUE }, // no leading number is behind those with leading number
{ it.shortName.toLowerCase() },
{ it.trailingNum },
)
}
}
fun String.parseName(): ComplexName {
val parts = split(" ")
return when (parts.size) {
1 -> ComplexName(null, this, null)
2 -> {
val (first, second) = parts
val firstNum = first.toIntOrNull()
when (firstNum) {
null -> ComplexName(null, first, second.toInt())
else -> ComplexName(firstNum, second, null)
}
}
else -> throw IllegalArgumentException("unsupported name format: $this")
}
}
You can run this there: https://pl.kotl.in/u9O2wBFgk
Alternatively, you could define Person with a ComplexName property in the first place to avoid computing it each time when comparing.
Note that you can technically implement a comparator with natural ordering with fancier parsing, generalizing the approach for numbers in any position. It would be trickier to deal with numbers inside words though, but that depends on your goal.
I have a vector of a custom struct and a list of attributes to use for ordering that vector in descending priority. For example:
struct TheStruct {
artist: String,
title: String,
date: String,
}
let order_vec: Vec<String> = vec!["artist".to_string(),"title".to_string(),"date".to_string()];
let item_vec: Vec<TheStruct> = Vec::new();
I want the vector to be ordered as given by order_vec. In this example, it should first be ordered by the artists name, when this is equal it should be ordered by the title. I do not want to hard-code this ordering as order_vec changes dynamically.
I found Vec::sort_by which takes a compare function. How do I dynamically generate that function? Is there a way to do this without sort_by?
How do I dynamically generate that function
You don't. You have a specific closure that has dynamic behavior inside of it.
Here, we have a list of sorts to apply. When we need to compare two items, we iterate through the list. We use Ordering::then_with to only apply the comparison when the previous comparison was Equal:
use std::cmp::Ordering;
#[derive(Debug, Copy, Clone)]
enum Field {
Artist,
Title,
Date,
}
struct TheStruct {
artist: String,
title: String,
date: String,
}
fn main() {
let mut items: Vec<TheStruct> = vec![];
use Field::*;
let orders = vec![Artist, Title];
items.sort_by(|a, b| {
orders.iter().fold(Ordering::Equal, |acc, &field| {
acc.then_with(|| {
match field {
Artist => a.artist.cmp(&b.artist),
Title => a.title.cmp(&b.title),
Date => a.date.cmp(&b.date),
}
})
})
})
}
I used an enum for the fields because I didn't want to deal with what to do when one of the sorts is an unknown field.
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
My program below gives me shortest country character count from a string array. How do I retrieve shortest country name at same time using Linq? So I want retrieve UK name at same time I find shortest country character count.
class Program
{
static void Main()
{
string[] countries = { "India", "USA", "UK" };
var minCount = countries.Min(x => x.Length);
var maxCount = countries.Max(x => x.Length);
Console.WriteLine
("The shortest country name has {0} characters in its name", minCount);
Console.WriteLine
("The longest country name has {0} characters in its name", maxCount);
}
}
An easy way is order your array by name Length:
string[] countries = { "India", "USA", "UK" };
var shortestCountry= countries.OrderBy(s=>s.Length).First();
With the shortestCountry you have both things that you need.
Another way could be using Aggregate extension method:
string[] countries = { "India", "USA", "UK" };
var shortestCountry = chrs2.Aggregate((seed, e) => seed.Length < e.Length ? seed : e);
Just order all countries by name length and take the first (for the shortest) and the last (for the longest):
string[] countries = { "India", "USA", "UK" };
var ordered = countries.OrderBy(x => x.Length);
var min = ordered.First();
var max = ordered.Last();
//"The shortest country name is UK, it has 2 characters in its name"
Console.WriteLine("The shortest country name is {0}, it has {1} characters in its name",
min, min.Length);
//"The longest country name is India, it has 5 characters in its name"
Console.WriteLine("The longest country name is {0}, it has {1} characters in its name",
max, max.Length);
I know this question already has an accepted answer which is perfectly adequate for the specific example given but anyone else reading this should keep in mind that it doesn't scale as well as it should. OrderBy results in an ordered sort of the data set which executes in O(n log n), whereas the problem can be solved with a single pass through the data set resulting in an execution order of O(n). I suggest downloading the morelinq library (also available via NuGet) which provides the MinBy extension to do precisely this.
Alternatively you can do yourself in O(n) with Aggregate as octaviocci has already pointed out.