Filter more times in a Java 8 Stream - list

Is it possible to filter more times in a Stream? For example if I have a list with IDs and I want to Stream a HashMap and map the key of the HashMap to key in a list and where they are match, I want to get the object from the HashMap and filter it again for example an int field in that object which is greater than 3 for example and sum them in the end. For example if its found 10 case where the list's key and HashMap's key are equal and it's filter those 10 cases and founds 3 case where for example an int field greater then 3 it gives back a sum of these in the end.
Here is my code so far:
When i try to print the list of the result of this i get this:
java.util.stream.ReferencePipeline$2#70177ecd
somemap.entrySet().stream().filter(e -> aListContainingIds.contains(e.getKey()))
.map(Map.Entry::getValue)
.map(n -> n.getTheOtherListFromMatchedValue().stream().filter(n -> n.getAnIntFieldFromObject() > 3))
.collect(Collectors.toList());

I think you should use Stream.flatMaptoInt (or Stream.flatMaptoLong) instead of just map:
int total = somemap.entrySet().stream()
.filter(e -> aListContainingIds.contains(e.getKey()))
.map(Map.Entry::getValue)
.flatMapToInt(value -> value.getTheOtherListFromMatchedValue().stream())
.filter(n -> n.getAnIntFieldFromObject() > 3)
.sum();
In general, flatMapToInt (or flatMap if the stream elements are instances of some class instead of primitives) is to be used when the function you are applying returns another stream, and you want the elements of that stream to be part to the original stream.
At the end, you can get the total with the IntStream.sum method.

Related

How to display interaction element with order way of list one in Kotlin

I have two lists and I want to return a result in the following way:
the result should contain elements that are in list one and list two
output should be same order as per first list
Input :
val first = listOf(1, 2, 3, 4, 5,7,9,15,11)
val second = listOf(2, 15 , 4,3, 11)
Output:
val output = listOf(2,3,4,15,11)
Please help me to learn how to get common values in both lists in order of list first in Kotlin.
You can do
val output = first.filter { second.contains(it) }
What you are looking for is the intersection of the two lists:
val output = first.intersect(second)
As pointed out by #Ivo the result is a Set which can be turned into a list with output.toList(). However, since the result is a set, it contains no duplicates, e.g. if first is listOf(1,2,3,1,2,3) and second is listOf(2,4,2,4), the result will be equal to setOf(2).
If this is not acceptable, the solution of #Ivo should be used instead.

How to generate the random values of map from a given set of values and then store the key and values into separate variables in scala

I am trying to generate the 1000 random key-val map pairs from the given(statically defined) 2 map key-val pairs in scala and also later i would want to break the key and value pairs and store them into separate variables
Whatever i have tried:
object methodTest extends App{
val testMap = Map("3875835"->"ABCDE","316067107"->"EFGHI")
def getRandomElement(seq: Map[String,String]): Map[String,String] = {
seq(Random.nextInt(seq.length))
}
var outList = List.empty[Map[String,String]]
for(i<-0 to 1000){
outList+=getRandomElement(testMap)
}
print(outList)
}
The Output should generate 1000 Key-val pairs of map like i am showing below
[3875835,ABCDE]
[316067107,EFGHI]
[3875835,ABCDE]
[316067107,EFGHI]
[316067107,EFGHI]
............
............
............. upto 1000 random key-val pairs
Please help me figure out where i am going wrong and let me know How to resolve it, if any issue regarding the requirement, please feel free to comment for it
You can transform your seed map testMap into sequence of key/value tuples using .toSeq and then generate key/value pairs by iterating over the list of numbers from 0 until 1000, associating each number to a random choice between first or second element of the seed:
import scala.util.Random
val testMap = Map("3875835" -> "ABCDE", "316067107" -> "EFGHI")
val seed = testMap.toSeq
val keyValuesList = (0 until 1000).map(index => seed(Random.nextInt(seed.size)))
Note: 0 until 1000 will return all the numbers from 0 to 1000 excluded, so 1000 numbers. If you use 0 to 1000 you will get all the numbers from 0 to 1000 included, so 1001 numbers.
If you want to print the resulting list, you can use .foreach method with println function as argument:
keyValuesList.foreach(println)
And you will get:
(3875835,ABCDE)
(316067107,EFGHI)
(3875835,ABCDE)
(3875835,ABCDE)
(316067107,EFGHI)
(3875835,ABCDE)
...
if you want to keep only the keys, you can iterate on the list using .map method, taking only the first element of the tuple by using ._1 method, that retrieve the first element of a tuple:
val keys = keyValuesList.map(keyValuePair => keyValuePair._1)
And if you want only a list containing all second elements of each pair:
val values = keyValuesList.map(keyValuePair => keyValuePair._2)

Enumerate list values into a list of dictionaries

I have a list of dictionaries, and I'm trying to assign dictionary key:value pairs based on the values of other other variables in lists. I'd like to assign the "ith" value of each variable list to ith dictionary in block_params_list with the variable name (as a string) as the key. The problem is that while the code appropriately assigns the values (as demonstrated by "pprint(item)"), when the entire enumerate loop is finished, each item in "block_params_list" is equal to the value of the last item.
I'm at a loss to explain this behavior. Can someone help? Thanks!
'''empty list of dictionaries'''
block_params_list = [{}] * 5
'''variable lists to go into the dictionaries'''
ran_iti = [False]*2 + [True]*3
iti_len = [1,2,4,8,16]
trial_cnt = [5,10,15,20,25]
'''the loops'''
param_list= ['iti_len','trial_cnt','ran_iti']#key values, also variable names
for i,item in enumerate(block_params_list):
for param in param_list:
item[param] = eval(param)[i]
pprint(item) #check what each item value is after assignment
pprint(block_params_list) #prints a list of dictionaries that are
#only equal to the very last item assigned
You've hit a common 'gotcha' in Python, on your first line of code:
# Create a list of five empty dictionaries
>>> block_params_list = [{}] * 5
The instruction [{}] * 5 is equivalent to doing this:
>>> d = {}
>>> [d, d, d, d, d]
The list contains five references to the same dictionary. You say "each item in 'block_params_list' is equal to the value of the last item" - that's an illusion, there's effectively only one item in "block_params_list" and you are assigning to it, then looking at it, five times over through five different references to it.
You need to use a loop to create your list, to make sure you create five different dictionaries:
block_params_list = []
for i in range(5):
block_params_list.append({})
or
block_params_list = [{} for i in range(5)]
NB. You can safely do [1] * 5 for a list of numbers, or [True] * 5 for a list of True, or ['A'] * 5 for a list of character 'A'. The distinction is whether you end up changing the list, or whether you change a thing referenced by the list. Top level or second level.
e.g. making a list of numbers, assinging to it does this:
before:
nums = [1] * 3
list_start
entry 0 --> 1
entry 1 --> 1
entry 2 --> 1
list_end
nums[0] = 8
after:
list_start
entry 0 -xx 1
\-> 8
entry 1 --> 1
entry 2 --> 1
list_end
Whereas making a list of dictionaries the way you are doing, and assigning to it, does this:
before:
blocks = [{}] * 3
list_start
entry 0 --> {}
entry 1 --/
entry 2 -/
list_end
first_block = blocks[0]
first_block['test'] = 8
after:
list_start
entry 0 --> {'test':8}
entry 1 --/
entry 2 -/
list_end
In the first example, one of the references in the list has to change. You can't pull a number out of a list and change the number, you can only put a different number in the list.
In the second example, the list itself doesn't change at all, you're assigning to a dictionary referenced by the list. So while it feels like you are updating every element in the list, you really aren't, because the list doesn't "have dictionaries in it", it has references to dictionaries in it.

Insertion order of a list based on order of another list

I have a sorting problem in Scala that I could certainly solve with brute-force, but I'm hopeful there is a more clever/elegant solution available. Suppose I have a list of strings in no particular order:
val keys = List("john", "jill", "ganesh", "wei", "bruce", "123", "Pantera")
Then at random, I receive the values for these keys at random (full-disclosure, I'm experiencing this problem in an akka actor, so events are not in order):
def receive:Receive = {
case Value(key, otherStuff) => // key is an element in keys ...
And I want to store these results in a List where the Value objects appear in the same order as their key fields in the keys list. For instance, I may have this list after receiving the first two Value messages:
List(Value("ganesh", stuff1), Value("bruce", stuff2))
ganesh appears before bruce merely because he appears earlier in the keys list. Once the third message is received, I should insert it into this list in the correct location per the ordering established by keys. For instance, on receiving wei I should insert him into the middle:
List(Value("ganesh", stuff1), Value("wei", stuff3), Value("bruce", stuff2))
At any point during this process, my list may be incomplete but in the expected order. Since the keys are redundant with my Value data, I throw them away once the list of values is complete.
Show me what you've got!
I assume you want no worse than O(n log n) performance. So:
val order = keys.zipWithIndex.toMap
var part = collection.immutable.TreeSet.empty[Value](
math.Ordering.by(v => order(v.key))
)
Then you just add your items.
scala> part = part + Value("ganesh", 0.1)
part: scala.collection.immutable.TreeSet[Value] =
TreeSet(Value(ganesh,0.1))
scala> part = part + Value("bruce", 0.2)
part: scala.collection.immutable.TreeSet[Value] =
TreeSet(Value(ganesh,0.1), Value(bruce,0.2))
scala> part = part + Value("wei", 0.3)
part: scala.collection.immutable.TreeSet[Value] =
TreeSet(Value(ganesh,0.1), Value(wei,0.3), Value(bruce,0.2))
When you're done, you can .toList it. While you're building it, you probably don't want to, since updating a list in random order so that it is in a desired sorted order is an obligatory O(n^2) cost.
Edit: with your example of seven items, my solution takes about 1/3 the time of Jean-Philippe's. For 25 items, it's 1/10th the time. 1/30th for 200 (which is the difference between 6 ms and 0.2 ms on my machine).
If you can use a ListMap instead of a list of tuples to store values while they're gathered, this could work. ListMap preserves insertion order.
class MyActor(keys: List[String]) extends Actor {
def initial(values: ListMap[String, Option[Value]]): Receive = {
case v # Value(key, otherStuff) =>
if(values.forall(_._2.isDefined))
context.become(valuesReceived(values.updated(key, Some(v)).collect { case (_, Some(v)) => v))
else
context.become(initial(keys, values.updated(key, Some(v))))
}
def valuesReceived(values: Seq[Value]): Receive = { } // whatever you need
def receive = initial(keys.map { k => (k -> None) })
}
(warning: not compiled)

Getting duplicates in dict

I have a dictionary, say d1 that looks like this:
d = {'file1': 4098, 'file2': 4139, 'file3': 4098, 'file4': 1353, 'file5': 4139}
Now, I've figured out how to get it to tell me if there are any dublicates or not. But what I'd like to get it to do is tell me if there are any, and what 2 (or more) values (and corresponding keys) are dublicates.
The output for the above would tell me that file1 and file3 are identical and that file2 and file5 are identical
I've been trying to wrap my head around it for a few hours, and haven't found the right solution yet.
try this to get the duplicates:
[item for item in d.items() if [val for val in d.values()].count(item[1]) > 1]
that outputs:
[('file3', 4098), ('file2', 4139), ('file1', 4098), ('file5', 4139)]
next sort the list by the second item in the tuple:
list = sorted(list, key=operator.itemgetter(1))
finally use itertools.groupby() to group by the second item:
list = [list(group) for key, group in itertools.groupby(list, operator.itemgetter(1))]
final output:
[[('file3', 4098), ('file1', 4098)], [('file2', 4139), ('file5', 4139)]]