Sort depending on variable in Scala - list

Is there any way in Scala to sort a list of objects by a specific field using a variable to set the order (ASC or DESC)?
I know with sortWith you can do something like
myList.sortWith((x, y) => x < y)
or
myList.sortWith((x, y) => x > y)
to sort ascending or descending, but I want to use a variable.
So, I tried something like this:
case class Person(firstName: String, LastName: String, age: Int)
private def sortDocuments(sortField: String, sortDirection: String, people: List[Person]): List[Person] = {
sortField match {
case "age" => people.sortBy(if (sortDirection == "desc") -_.age else _.age)
case "firstName" => people.sortWith { sortString(a.firstName, b.firstName, sortDirection) }
case "lastName" => people.sortWith { sortString(a.firstName, b.lastName, sortDirection) }
}
}
private def sortString(fieldA: String = null, fieldB: String = null, direction: String = "asc") = {
val fieldAVaild = Option(fieldA).getOrElse("")
val fieldBVaild = Option(fieldB).getOrElse("")
if (direction == "desc") fieldBVaild > fieldAVaild else fieldAVaild < fieldBVaild
}
But sortWith only receives a function with two parameters, so I get an error when I add the third parameter (sortDirection).

You forgot the (a, b) => expr part of the first/last name cases
case "firstName" => people.sortWith {(a, b) => sortString(a.firstName, b.firstName, sortDirection) }

Related

Obtain values that match from several conditions on a list in javascript

I'm trying to obtain a list from a list in Javascript.
This is the list:
const cars = [
{
id: 1,
brand: "Mercedes Benz",
properties: [
{
property: "Mechanical",
value: 2,
},
{
property: "Chemical",
value: 2,
},
{
property: "Pressure",
value: 3,
}],
},
{
id: 2,
brand: "BMW",
properties: [
{
property: "Mechanical",
value: 5,
},
{
property: "Chemical",
value: 3,
},
{
property: "Pressure",
value: 6,
}],
}
]
I need the cars which match some properties property with a value greater than X, Y
For example, I want the cars which Mechanical properties have a value greater than 3 and a Pressure greater than 4. In that case I'll obtain the complete object with id 2.
Does anyone have an idea? That is having me a hard time
Tip: I paste it on a Node REPL ;)
This is what I tried but I obtain nothing:
cars.filter(car => car.properties.some((p1, p2) => {return ((p1.property === "Mechanical" && p1.value > 3) && (p2.property === "Pressure" && p2.value > 4))}))
Thanks in advance
You need to iterate all items and check each one for it's relevant condition, and if all items pass, return true. In your case you are checking each item for all conditions, and since no item's property can have both "Mechanical" and "Pressure" values at the same time, all fail.
When an array needs to pass all conditions, you should use Array.every() that will only return true, if all iterated items would return true.
To make this more generic, we can store the conditions as functions in an object or a Map. If there is a condition function for this property, we'll use the function to check the value. If not, we can return true immediately.
Note: this answer uses Optional chaining (?.) and the Nullish coalescing operator (??) to return true if the predicate doesn't exist. If your running environment doesn't support this operators replace the line with predicate[property] ? predicate[property](value) : true (see 2nd example).
const fn = (predicate, cars) =>
cars.filter(car => car.properties.every(({ property, value }) =>
predicate[property]?.(value) ?? true
))
const cars = [{"id":1,"brand":"Mercedes Benz","properties":[{"property":"Mechanical","value":2},{"property":"Chemical","value":2},{"property":"Pressure","value":3}]},{"id":2,"brand":"BMW","properties":[{"property":"Mechanical","value":5},{"property":"Chemical","value":3},{"property":"Pressure","value":6}]}]
const predicate = {
Mechanical: value => value > 3,
Pressure: value => value > 4,
}
const result = fn(predicate, cars)
console.log(result)
Or using a ternary:
const fn = (predicate, cars) =>
cars.filter(car => car.properties.every(({ property, value }) =>
predicate[property] ? predicate[property](value) : true
))
const cars = [{"id":1,"brand":"Mercedes Benz","properties":[{"property":"Mechanical","value":2},{"property":"Chemical","value":2},{"property":"Pressure","value":3}]},{"id":2,"brand":"BMW","properties":[{"property":"Mechanical","value":5},{"property":"Chemical","value":3},{"property":"Pressure","value":6}]}]
const predicate = {
Mechanical: value => value > 3,
Pressure: value => value > 4,
}
const result = fn(predicate, cars)
console.log(result)

How to convert ListBuffer to List (immutable collection ) in scala

I have the below code using ListBuffer but I want to use immutable collection like List and achieve the same result
val list1: ListBuffer[String] = ListBuffer("a", "b", "c")
val s= list1
.foldLeft(mutable.ListBuffer.empty[String]) { (strings, content) =>
{
if(StringUtils.isNotBlank(content))
strings += content
else
strings += "-"
}
}
.mkString(";")
Second version
val list1: ListBuffer[String] = ListBuffer("a", "b", "c")
val s= list1
.foldLeft(mutable.ListBuffer.empty[String]) { (strings, content) =>
{
strings += content
}
}
.mkString(";")
You can use collect
List("a", "b", "c").collect {
case c if StringUtils.isNotBlank(c) => c
case _ => "-"
}.mkString(";")

Extract string from a long string in scala

I want to extract the useful fields from a string object like the following one
Request(Some(8454439),Some(16872692),Some(0.0.0.0),Some(8281008),Some(ArrayBuffer(845434399)),Some(129032),Some(3),Some(Profile),Some(en),None,None,None,None,Some(true),None,Some(Food),None,Some(Fish))
It has 18 fields in total, and what I want to do is assign them to 18 different strings and extract useful info if it is Some(X), otherwise set the string to None.
For example in this case, the string array in the response should be
val results = Array("8454439", "16872692", "0.0.0.0", "8281008", "ArrayBuffer(845434399)",
"129032", "3", "Profile", "en", "None", "None", "None", "None", "true", "None",
"Food", "None", "Fish")
If you can get the list of items somehow, you could do something like this with a Seq[Option[Any]]:
val items: Seq[Option[Any]] = ???
items.map(_.getOrElse("None").toString)
But if you only have the output of Request.toString, this will get you most of the way there:
val s = "Request(Some(8454439),Some(16872692),Some(0.0.0.0),Some(8281008),Some(ArrayBuffer(845434399)),Some(129032),Some(3),Some(Profile),Some(en),None,None,None,None,Some(true),None,Some(Food),None,Some(Fish))"
val pat1 = """Some\([\w.()]+?\)|None""".r
val pat2 = """Some\((.*)\)""".r
pat1.findAllIn(s).map {
case pat2(some) => some
case x => x
}.toList
// res0: List[String] = List(8454439, 16872692, 0.0.0.0, 8281008, ArrayBuffer(845434399, 129032, 3, Profile, en, None, None, None, None, true, None, Food, None, Fish)
My regex-fu isn't strong enough to keep the trailing parenthesis on the ArrayBuffer value, but otherwise this seems to work.
Have you tried something along the lines of
val items = request.map {
case Some(value) => value
case None => "None"
}
To actually convert Some(ArrayBuffer(845434399)) to "ArrayBuffer(845434399)" though, you may need a nested match statement:
val items = request.map {
case Some(value) => value match {
case strng: String => strng
case other => ???
}
case None => "None"
}
Off the top of my head, not sure what to call to do it, but maybe one of the functions of the Any type would be of help.

Scala : how to expose regex variables bound within a case statement

Scala regex works great under either of two conditions:
unconditionally executed code:
e.g.
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
val anotherregx = """([\w]+)\t([/\w+=\-!%# ]+)""".r
val lineregx(category, aaUrl, title)
or if inside a case statement we consume the expressions (and don't need them again..)
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
line match {
case lineregx(category, aaUrl, title) => // do something with category, aaUrl and title in here!
case anotherRegx(category, aaUrl) => // do something different with category, aaUrl and title in here!
case _ => { println("did not match line %s".format(line)); return 0 }
}
But what about if i need to 'surface' the matches to variables outside of the case statement? Specifically the var's shown below,
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
var category = "" ; var aaUrl = "";var title = ""
line match {
case lineregx(category, aaUrl, title) => val lineregx(category, aaUrl, title) = line
case anotherRegx(category, aaUrl) => val lineregx(category, aaUrl) = line
case _ => { println("did not match line %s".format(line)); return 0 }
}
// Do something with category, aaUrl, title HERE after the case statement.
Problem is , the syntax for applying the lineregx/anotherregex makes those variables local to the case statement only.
Roughly,
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
val (category, aaUrl, title) = line match {
case lineregx(category, aaUrl, title) => (category, aaUrl, title)
case anotherRegx(category, aaUrl) => (category, aaUrl, ???)
case _ => { println("did not match line %s".format(line)); return 0 }
}
// Do something with category, aaUrl, title HERE after the case statement.
But that code is quite disorganized. For one thing, there's the question of the value of title for the second case. For another, there's the early return. Instead, the code would probably be best organized like this:
// method declaration
// ...
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
line match {
case lineregx(category, aaUrl, title) => f(category, aaUrl, title)
case anotherRegx(category, aaUrl) => f(category, aaUrl, ???)
case _ =>
println("did not match line %s".format(line))
0
}
} // end of method
def f(category: String, aaUrl: String, title: String): Int = {
// Do something with category, aaUrl, title HERE
}
You could use Option:
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
val (maybeCat, maybeUrl, maybeTitle) =
line match {
case lineregx(category, aaUrl, title) => (Some(category), Some(aaUrl), Some(title))
case anotherRegx(category, aaUrl) => (Some(category), Some(aaUrl), None)
case _ =>
println("did not match line %s".format(line))
(None, None, None)
}
var category = maybeCat getOrElse ""
var aaUrl = maybeURL getOrElse ""
var title = maybeTitle getOrElse ""
Slightly more verbose, but this way you can get the variables in the same scope.

Lists AS value of a Map in Dart

I want to create a map of members, but every membres have 3 propreties : first name, last name, and username. How can I create like a list of liste, but with a map.
So I want to have something like :
var membres= {['lastname': 'Bonneau',
'firstname': 'Pierre',
'username': 'mariobross'],
['lastname': 'Hamel',
'firstname': 'Alex',
'username': 'Queenlatifa'],
};
As you know, this code doesn't work. But it explain pretty well what I am trying to do.
I think you are confusing the two constructs here.
Read this introduction to the language: http://www.dartlang.org/docs/dart-up-and-running/ch02.html#lists
A list is a list of elements which can be denoted with the shorthand [...] syntax:
var list = [1, 2, "foo", 3, new Date.now(), 4];
Whereas a map can be denoted with the curly brace shorthand syntax:
var gifts = { // A map literal
// Keys Values
'first' : 'partridge',
'second' : 'turtledoves',
'fifth' : 'golden rings'
};
So, let's modify your code to work:
var members = [
{
'lastname': 'Bonneau',
'firstname': 'Pierre',
'username': 'mariobross'
},
{
'lastname': 'Hamel',
'firstname': 'Alex',
'username': 'Queenlatifa'
}
];
You can, for example, print the information like this:
members.forEach((e) {
print(e['firstname']);
});
If I understand your intent correctly, you want to have a list of maps. What you have is correct except you confused [ and {. The following works:
var membres = [
{'lastname': 'Bonneau',
'firstname': 'Pierre',
'username': 'mariobross'},
{'lastname': 'Hamel',
'firstname': 'Alex',
'username': 'Queenlatifa'}
];
As an example, to get a list of all usernames:
print(membres.map((v) => v['username']));
If you don't really need a Map, what about using a class to improve the structure of your code :
class Member {
String firstname;
String lastname;
String username;
Member(this.firstname, this.lastname, this.username);
}
main() {
final members = new List<Member>();
members.add(new Member('Pierre', 'Bonneau', 'mariobross'));
members.add(new Member('Alex', 'Hamel', 'Queenlatifa'));
// use members
}
You mean like this?
// FirstName => LastName => Value
var lookup = new Map<String, Map<String, String>>();
// get / set values like this
void setValue(String firstName, String lastName, String value) {
if (!lookUp.containsKey(firstName))
lookUp[firstName] = new Map<String, String>();
lookUp[firstName][lastName] = value;
}
String getValue(String firstName, String lastName) {
if (!lookUp.containsKey(firstName)) return "";
return lookUp[firstName][lastName];
}
First of all you need to create a map with value as list. Dont forget to initialize it
then if you want to fill it you first need to use built in function like putIfAbsent as in dart to add first object in list and then use update to add items in list. therefore you will need two arrays. First to put elements and then to add elements in list with same key. Also you can use try catch to identify if the key is present or not to do that in one loop
for (var item in days) {
var date_time = DateTime.parse(item["date"] + " 00:00:00");
_events[date_time] = _events.putIfAbsent(
date_time,
() => [
{
"title": item["title"],
"date": item["date"],
"time": reUse.get_time_am_pm_format(item["time"]),
"feature": item["feature"],
}
]);
}
for (var item in days) {
var date_time = DateTime.parse(item["date"] + " 00:00:00");
_events[date_time] = _events.update(date_time, (value) {
value.add({
"title": item["title"],
"date": item["date"],
"time": reUse.get_time_am_pm_format(item["time"]),
"feature": item["feature"],
});
return value;
});
}