new to scala so missing basic of list.
I'm trying to make a list of following class
case class Person (val name: String, val age:Int, val email: String)
How do I make a list immutable list of Person class?
I tried:-
val list: List[Person] = List(Person("",0,"")) // compilation ok
list.::(person) // compile ok
But when I check size it is 1. It should be 2. Also I don't see the added person in the list.
Please let me know how do I make list of Person class, inset new person and then retrieve person if either age or name or email or by index as well.
Please don't refer me to api doc http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List
It is hard to understand :(
Try this.
val list2 = list.::(person)
Remember list is immutable.
list2 should have size 2.
Keeping in mind immutable lists as aforementioned, for
case class Person (val name: String = "",
val age:Int = 0,
val email: String = "")
(note you can set construction defaults to each field) and
val list: List[Person] = List(Person("",0,"")) // compilation ok
we can construct a second List[Person] also with the List concatenation operator ++ as follows,
val list2 = list ++ List(Person(age=1))
list2: List(Person(,0,), Person(,1,))
To retrive a Person by a given criterium, for instance in this example, by age, consider collect as follows,
val list3 = list2.collect { case p#Person(_, age, _) if age == 1 => p }
List[Person] = List(Person(,1,))
Here we extract the field of interest, age, and check for a condition (whether it equates to 1), thus we select those entries in list2 that hold the condition; to deliver such entries, we use a bind p on each Person instance; perhaps more canonical (and unnecessarily repetitive) approach would be
val list3 = list2.collect {
case Person(name, age, email) if age == 1 => Person(name, age, email) }
A similar approach relies in filter as follows,
list2.filter(p => p.age == 1)
And with a for comprehension, like this
for ( p#Person(n, a, e) <- list2 if a == 1 ) yield p
A more elaborated approach to defining a selection over a list of entries includes the definition of a method that takes a predicate as argument and evaluates it against each entry; in addition this can be encapsulated in an implicit class for ease of use; thus consider this example,
implicit class PersonsOps( val persons: List[Person] ) extends AnyVal {
def select(pred: Person => Boolean) = persons.filter(pred)
}
and so we can define an anonymous function in the call to byAge, namely (x: Person) => x.age == 1; hence
list2.select( (x: Person) => x.age == 1 )
or equivalently
list2.select( _.age == 1 )
Note in this illustrative example select and filter are equivalent or interchangeable, yet select may be enriched/extended with additional requirements.
As Soumya Simanta said, you have an immutable list, so you have to assign the List returned by the :: operator to a new val. By the way you can use following notation
val list2 = person :: list
which is equivalent to
val list2 = list.::(person)
To get the information of your Person instances you can use the following line:
list2 foreach { p: Person => println(p.email) }
foreach is a method of List. It applies a function (e.g. { p: Person => println(p.email) }) to all elements of the list.
list.::(person)
retrieves a new list, containing both elements: the old and the new. However, the operation does not change your list called list. Try this way:
var list: List[Person] = List(Person("",0,"")) // compilation ok
list = list.::(person) // compile ok
Related
I'd like to know how to name objects in a list by a field
case class age(id: Int,name:String,age:Int)
val people: List[age] = List(age(2,"Angela",31),age(3,"Rick",28))
In this minimum example, I'd like to create objects Angela and Rick.
My initial idea:
val objects: List[age] = people.map( x => {val x.name = new age(x.name,x.age) })
But of course val x.name doesn't work because u can't use a variable name in a variable name.
This isn't an actual problem on a project but rather a concept I am stuck on.
It's not clear what's your intent. Do you want to create variables named angela and rick? You can do it manually for small number of list element and for large number of list element this doesn't make sense, because how would you use your 100 variables?
It seems you are talking about some mapping from names to properties and then Map will probably suit you the best
val peopleMap: Map[String,age] = people.map(p => p.name -> p). // this will create list of pairs
toMap // this will turn it to a Map
pritnln(peopleMap("Angela")) // now you can use person name to get all the info about them
A simple solution is to use a map:
case class Person(id: Int, name: String, age: Int)
val people: List[Person] = List(Person(2, "Angela", 31), Person(3, "Rick", 28))
val peopleByName: Map[String, Person] = people // List[Person]
.map(p => (p.name, p)) // List[(String, Person)]
.toMap // Map[String, Person]
or, starting with a Map instead of a List:
case class Person(id: Int, age: Int)
val peopleByName: Map[String, Person] = Map(
"Angela" -> Person(2, 31), // (String, Person)
"Rick" -> Person(3, 28) // (String, Person)
) // Map[String, Person]
However, if you want to define a member at runtime, then you'll have to extend the Dynamic trait (code snippet from here, the import is mine (required, otherwise the compiler won't be happy)):
import scala.language.dynamics
class DynImpl extends Dynamic {
def selectDynamic(name: String) = name
}
scala> val d = new DynImpl
d: DynImpl = DynImpl#6040af64
scala> d.foo
res37: String = foo
scala> d.bar
res38: String = bar
scala> d.selectDynamic("foo")
res54: String = foo
If you really want to do this, then I suggest this implementation:
class DynamicPeople(peopleByName: Map[String, Person]) extends Dynamic {
def selectDynamic(name: String) = peopleByName(name)
}
I am trying to create a new list that combines the contents of one single-level list with the hierarchy of another nested list and link them by the IDs of objects in both lists. Note: the ID for the BChain object is stored within the ItemObj inside it.
I have three objects:
ItemObj(id: String, stored: DateTime)
BChain(item: ItemObj, List[BChain])
AChain(aid: String, List[AChain])
I have two lists:
val nestedList: List[AChain]
val singleLevelList: List[BChain]
I want the second list to have the hierarchy of the first, but still contain it's own elements. Therefore, BChain should include the original ItemObj and List[BChain] properties (including all of ItemObj's original property data - ID and DateTime) when it is put into the newly desired list output.
So Instead of (input):
val nestedList = List(
AChain("123", List(AChain("456", [])),
AChain("789", [])
)
val singleLevelList = List(
BChain(ItemObj("123", DateTime), []),
BChain(ItemObj("456", DateTime), []),
BChain(ItemObj("789", DateTime), []))
)
I would like the following output:
val combinedLists = List(
BChain(ItemObj("123", DateTime), List(BChain(ItemObj("456", DateTime), [])),
BChain(ItemObj("789", DateTime), [])
)
as the final list.
Note: There may be more items in the nested list than the single-level list, and if this is the case then the extra items should be ignored. Each of the items in the single-level list should correspond to one of the items in the nested list.
How can I accomplish this?
Any help would be appreciated.
First I had to dummy up a DateTime type and value so that the following would compile.
case class ItemObj(id: String, stored: DateTime)
case class BChain(item: ItemObj, bcl :List[BChain])
case class AChain(aid: String, acl :List[AChain])
Then I changed your singleLevelList into real Scala code and created a Map for fast lookup.
val singleLevelList = List(
BChain(ItemObj("123", DateTime), Nil),
BChain(ItemObj("456", DateTime), Nil),
BChain(ItemObj("789", DateTime), Nil)
)
val sLLMap = singleLevelList.groupBy(_.item.id)
Next a recursive method to change all AChains to BChains.
def a2b(aLst :List[AChain]) :List[BChain] =
aLst.map(a => BChain(sLLMap(a.aid).head.item, a2b(a.acl)))
Now to test it.
val nestedList = List(
AChain("123", List(AChain("456", Nil))),
AChain("789", Nil)
)
a2b(nestedList) //appears to work
Of course this will throw if nestedList has an A without a corresponding B in the singleLevelList.
Maybe something like this is what you want?
final case class ItemObj(id: String, stored: DateTime)
final case class BChain(item: ItemObj, list: List[BChain])
final case class AChain(aid: String, list: List[AChain])
def combine(as: List[AChain], bs: List[BChain]): List[BChain] = {
val asMap = as.iterator.map(a => a.aid -> a.list).toMap
def toBChain(a: AChain): BChain =
BChain(
item = ItemObj(id = a.aid, stored = ???),
list = a.list.map(toBChain)
)
bs.map {
case BChain(item, list) =>
val newElements =
asMap
.getOrElse(key = item.id, default = Nil)
.map(toBChain)
BChain(
item,
list ::: newElements
)
}
}
This code will work even if root element of nested list is absent in single level list
final case class ItemObj(id: String, stored: DateTime = null)
final case class AChain(aid: String, children: List[AChain] = List())
final case class BChain(bid: ItemObj, children: List[BChain] = List())
val nestedList = List(
AChain("123", List(AChain("456"))),
AChain("789")
)
def getCombineList(nestedList: List[AChain], singleList: List[BChain]): List[BChain] = {
val singleListMap = singleList.groupBy(_.bid.id)
def combine(items: List[AChain]): List[BChain] = {
items flatMap {item =>
val children = combine(item.children)
val parent = singleListMap.get(item.aid).map(v =>
List(v.head.copy(children = children))
).getOrElse(
children
)
parent
}
}
combine(nestedList)
}
val s = getCombineList(nestedList, List(BChain(ItemObj("456")), BChain(ItemObj("789"))))
I am facing a very weird problem while getting elements of a list
Below is the piece of code where I am passing arguments as "bc" and "mn"
val list1 = List("abc", "def", "mnp")
val list2 = List(args(0), args(1))
val header1=list1.filter(x => list2.exists(y => x.contains(y)))
println(header1)
Output-List("abc","mnp")
I am trying to do it in a different way (by passing the same arguments)but getting an empty List
val list1 = List("abc", "def", "mnp")
//val list2 = List(args(0), args(1))
val ipList1= new ListBuffer[Any]
for(i <- 0 to 1){
ipList1 +=args(i)
}
val list2=ipList1.toList
println(list2)
val header1=list1.filter(x => list2.exists(y => x.contains(y)))
println(header1)
Output-List(bc, mn)
List()-->This is the empty List I am getting
Can Someone please tell where I am doing it wrong and How to make it right?
The problem is that x.contains(y) does not mean what you think it means. String has a contains method that checks whether another String is a substring of this String. But in your code y doesn't have type String, but type Any. So the contains method of String isn't called. It's the contains method of WrappedString which treats the String x as though it's a Seq[Char]. That method doesn't check whether any substring is equal to y but whether any character is equal to y.
The solution, obviously, is to use a ListBuffer[String].
The problem is that you are using a ListBuffer[Any] thus the elements lost their type information from String to Any and apparently that changes the semantics of the code.
You may either do this:
val ipList1 = new ListBuffer[String]
for (i <- 0 to 1) {
ipList1 += args(i).toString
}
val list2 = ipList1.toList
Or even better just:
val list2 = args.slice(0, 2).toList
I have a List[T] where the datas inside are like below:
[0]- id : "abc", name: "xyz", others: "value"
[1]- id : "bcd", name: "dsf", others: "value"
Now I want to return the same List[T] but with the id and names i.e the returning List will be:
[0]- id : "abc", name: "xyz"
[1]- id : "bcd", name: "dsf"
I tried with below code:
var temps = templates.map(x => List("id" -> x.id, "name" -> x.name))
But it it produces List inside List i.e:
[0]-
[0] id : "abc"
[1] name: "xyz"
[1]-
[0] id : "bcd"
[1] name: "dsf"
I tried with tuple also but in vain. How can i just map my list such that everything is cleaned out except the id and name value pair??
Unless you want to define a new class with just the id and name fields, I think tuples would be your best bet:
scala> case class obj(id: String, name: String, others: String)
defined class obj
scala> val l = List(new obj("abc", "xyz", "value"), new obj("bcd", "dsf", "value"))
l: List[obj] = List(obj(abc,xyz,value), obj(bcd,dsf,value))
scala> l.map(x => (x.id, x.name))
res0: List[(String, String)] = List((abc,xyz), (bcd,dsf))
Also you are actually using tuples in your example, the -> syntax creates tuples in Scala:
scala> "a" -> "b"
res1: (String, String) = (a,b)
Here is the "define another class" option:
scala> case class obj2(id: String, name: String){
| def this(other: obj) = this(other.id, other.name)
| }
defined class obj2
scala> l.map(new obj2(_))
res2: List[obj2] = List(obj2(abc,xyz), obj2(bcd,dsf))
Given that the List[T] is a List[Map], then you may able to do the following:
//Remove the "others" key.
val temps = templates.map( map => {map - "others"})
I think its pretty clear that your list contains objects with three members: id, name, others. Hence you access them by using
{x => x.name}
What I am not so sure about, is how you imagine your end result. You obviously need some data structure, that holds the members.
You realized yourself, that its not very nice to store each objects members in a list inside the new list, but you seem to not be ok with tuples?
I can imagine, that tuples are just what you want.
val newList = oldList.map{e => e.id -> e.name}
results in alist like this:
List(("id_a", "name_a"), ("id_b", "name_b"), ("id_c","name_c"))
and can be accessed like this(for example):
newList.head._1
for the first tuples ids, and
newList.head._2
for the first tuples name.
Another option could be mapping into a map, since this looks pretty much like, what you want in the first place:
newMap = oldList.map{e => e.id -> e.name}.toMap
This way you can access members like this newMap("key") or safer: newMap.get("key") which returns an option and wont end up in an exception, if the key doesn't exist.
I am just trying to figure out how immutable things like a List are working, and how I can add things to it?
I am very sorry for asking such dumb questions, but why is here my list always empty when printing it out?
var end = false
val list = List()
while (!end) {
val input = scala.io.StdIn.readLine("input:")
if (input == "stop" ) end = true
else input :: list
}
println(list)
}
Sorry for my inconvenience and this rather stupid question!
I am just trying to figure out how immutable things like a List are working, and how I can add things to it?
You can't. That's what immutable means, after all. If Latin is not your cup of tea, the English translation of immutable is unchangeable. It should be clear now, why you can't change something that is unchangeable.
I am very sorry for asking such dumb questions, but why is here my list always empty when printing it out?
You create an empty list, and you never change it (because it cannot be changed anyway). So, of course it is empty.
What can you can do, however, is create a new list which is almost exactly like the old list, except with a new item prepended to the front. That's what you are doing here:
input :: list
However, you don't assign this new list anywhere, you don't return it, you completely ignore it.
If you want to actually use your list in any way, you need to remember it somehow. The most obvious solution would be to assign it to a variable:
var end = false
var list: List[String] = List() // note: `var` instead of `val`
while (!end) {
val input = scala.io.StdIn.readLine("input:")
if (input == "stop" ) end = true
else list = input :: list // note: assign to `list`
}
println(list)
However, that's not very idiomatic. After all, we have now taken an immutable list and assigned it to a mutable variable … IOW, we have just moved the mutability around.
Instead, we could use a recursive solution:
def buildListFromInput(list: List[String] = List()): List[String] = {
val input = scala.io.StdIn.readLine("input:")
if (input == "stop") list else buildListFromInput(input :: list)
}
println(buildListFromInput())
This solution is not only recursive, the recursive call is also in tail position (IOW, the method is tail-recursive), which means that it will be just as efficient as a while loop (in fact, it will be compiled into a while loop, or more precisely, into a GOTO). The Scala Language Specification guarantees that all implementations of Scala must eliminate direct tail-recursion.
The reason
println(list)
is only printing out an empty list is because the bit
input :: list
isn't actually mutating the list itself. It is simply, in this case, very temporarily, creating a list containing the input at the front.
Try
println(input :: list)
or
val newList = input :: list
println(newList)
and you'll see what I mean.
Try rewriting the code in more functional way. Every operation on Immutable data structures return new instance with change. So :: operator creates new List with input on front. You might want to try rewrite this code as tail recursive function as follows.
#tailrec
def scanInput(continue: Boolean,acc: List[String]): List[String] = {
val input = scala.io.StdIn.readLine("input:")
if(!continue) acc
else scanInput(input != "stop", input :: acc)
}
Above code has no mutating state and it suits more Scala functional style.
In scala List is immutable.
Then how can I add items to the list?
When you add an item to list a new List instance is crated with a item as its head and its tail now contains the previous list.
If you have list of "1,2,3" called intList internally it is represented as
List(3, List(2, List(1, Nil) ) )
If you add an element 4 to this intList
List(4, intList )
Lets call this newList
Note intList still contains List(3, List(2, List(1, Nil) ) ).
If you want the intList to refer the newList You will have to do
intList = intList.add(4)
How can I fix my code
Change list from val to var. Then you can assign resulting List to list variable
list = input :: list
Source: Online course on Scala called Functional Programming Principles in Scala
Thank you for all your help, I appreciate your help so much from all of you!
I should have taken a closer look at recursion since it seems to be really important like in Scala!
But trough your help I am getting an better idea of how it works!
I just tried to figure out how your solutions are working and created my own:
val list = List()
def scanInput(acc: List[String]): List[String] = {
val input = scala.io.StdIn.readLine("input:")
input match {
case "stop" => acc
case _ => scanInput(input :: acc)
}
}
println(scanInput(list))