I have a List of arrays containing two Strings:
List<String[]> pets = {["Alice", "dog"], ["Bob", "horse"], ["Alice", "cat"], ["Bob", "cat"]}
I want to turn them into a Map from String to List of Strings, like the following:
Map<String, List<String>>> pets = Map where
"Alice" maps to the List {"dog, "cat"}
"Bob" maps to the List {"horse", "cat"}
i.e. we take the first element of the array and group together all elements where the first elements are equal to each other. The imperative way of solving this would be to loop over the elements, and gradually building a Map. Is there any way to do this with java Streams, ie using a Collector or reduce?
Sure, you can!
You can group by the first element of each array which will be the map keys then apply the mapping collector to get the corresponding values.
Map<String, List<String>> resultSet = pets.stream()
.collect(groupingBy(array -> array[0],
collectingAndThen(mapping(e -> Arrays.copyOfRange(e, 1, e.length),
toList()),
f -> f.stream().flatMap(Arrays::stream)
.collect(toList()))));
or:
Map<String, List<String>> resultSet = pets.stream()
.collect(groupingBy(array -> array[0],
flatMapping(e -> Arrays.stream(Arrays.copyOfRange(e, 1, e.length)),
toList())));
Note, this solution uses the flatMapping collector which is only available as of JDK-9.
Related
How to print the distinct element from the Array Stream in java 8?
I am playing with Java-8 and trying to understand how it works with distinct.
Collection<String> list = Arrays.asList("A", "B", "C", "D", "A", "B", "C");
// Get collection without duplicate i.e. distinct only
List<String> distinctElements = list.stream().distinct().collect(Collectors.toList());
//Let's verify distinct elements
System.out.println(distinctElements);
// Array Stream
System.out.println("------------------------------");
int[] numbers = {2, 3, 5, 7, 11, 13, 2,3};
System.out.println(Arrays.stream(numbers).sum()); // ==> Sum
System.out.println(Arrays.stream(numbers).count()); // ==> Count
System.out.println(Arrays.stream(numbers).distinct()); // ==> Distinct
The last line Just merely gives me reference of object, I want actual values
[A, B, C, D]
------------------------------
46
8
java.util.stream.ReferencePipeline$4#2d98a335
You don't see distinct values directly because IntStream.distinct() is not a terminal operation and it returns IntStream as stated in the documentation:
Returns a stream consisting of the distinct elements of this stream.
You have to terminate your stream, similarly to code you already have in your example:
List<String> distinctElements = list.stream()
.distinct()
.boxed()
.collect(Collectors.toList());
Here you call Stream.collect(Collector<? super T,A,R> collector) method which is a terminal operation and you get a list of distinct elements in return.
Both Stream.count() and IntStream.sum() are terminal operations and they perform calculation right away, closing your stream and returning a value.
Arrays.stream() normally returns a Stream, but it has an overloaded version: stream(int[] array), which returns an IntStream, which is a stream of primitive ints. IntStream.distinct() returns an IntStream as well.
In order to collect it, you could use collect(Collectors.toList()):
Arrays.stream(numbers)
.distinct()
.boxed()
.collect(Collectors.toList());
You could also store the result into an int[]:
Arrays.stream(numbers)
.distinct()
.toArray();
Assume you have a list of items, each with a set of attributes.
What is an efficient algorithm for generating all pairs from the list having the same attributes?
For example, given a list:
[('item1', {'a','b'}), ('item2', {'a'}), ('item3', {'c','b'}), ('item4', {'b'})]
We should return the following list of four pairs, out of the total possible six:
('item1', 'item2') # both have attribute 'a'
('item1', 'item3') # both have attribute 'b'
('item1', 'item4') # both have attribute 'b'
('item3', 'item4') # both have attribute 'b'
Now, the trivial approach would be to first generate the list of all possible n(n+1)/2 pairs, and then filter out those without similar attributes, but I suspect this approach is inefficient, especially if the number of pairs is very large.
Any suggestions?
I would suggest a two phase algorithm:
arr = [('item1', {'a','b'}), ('item2', {'a'}), ('item3', {'c','b'}), ('item4', {'b'})]
# 1. create map with for each attribute the list of items that have it
mp = {}
for lst in arr:
for prop in lst[1]:
if prop not in mp: mp[prop] = []
mp[prop].append(lst[0])
# 2. for each attribute: add the pairs of items to the result set
result = set()
for prop in mp:
items = mp[prop]
# collect all pairs in items list
for p1 in range(len(items)):
for p2 in range(p1+1,len(items)):
result.add((items[p1],items[p2]))
print (result)
Output:
{('item1', 'item4'), ('item1', 'item2'), ('item3', 'item4'), ('item1', 'item3')}
I've got an array of objects which I pull from the database. But I can sort them only in ascending or descending order from database, however I need them in custom order.
Let's say I have an array of objects from db :
arr = [obj1,obj2,obj3]
where obj1 has id 1, obj2 has id 2 and obj3 has id 3
but my sort order would be 3,1,2 or I'd have some array of ids which would dictate the order i.e [3,1,2]
So the order of custom sorting would be :
arr = [obj3,obj1,obj2]
I've tried :
arr.sort_by{|a,b| [3,1,2]}
I've been reading some tutorials and links about sorting and it's mostly simple sorting. So how would one achieve the custom sorting described above?
You're close. [3,1,2] specifies an ordering, but it doesn't tell the block how to relate it to your objects. You want something like:
arr.sort_by {|obj| [3,1,2].index(obj.id) }
So the comparison will order your objects sequentially by the position of their id in the array.
Or, to use the more explicit sort (which you seem to have sort_by slightly confused with):
arr.sort do |a,b|
ordering = [3,1,2]
ordering.index(a.id) <=> ordering.index(b.id)
end
This is like #Chuck's answer, but with O(n log n) performance.
# the fixed ordering
ordering = [3, 1, 2]
# a map from the object to its position in the ordering
ordering_index = Hash[ordering.map(&:id).each_with_index.to_a]
# a fast version of the block
arr.sort_by{|obj| ordering_index[obj.id]}
I would like to sort a dictionary where I have a string key but my values are lists of tuples. For example, imagine we have a dictionary where each person is mapped to their rating of different academic subjects, where d.items() would return:
('Person':[("Math",5),("Chemistry",10),("History",2)])
Is there any way I can sort the value of each key alphabetically? For example, d['Person'] would now return:
('Person':[(Chemistry",10),("History",2),("Math",5)])
My solution:
arr = {
'Person': [("Math",5),("Chemistry",10),("History",2)]
}
def customKey(a):
return a[0]
for i in arr.keys():
arr[i] = sorted(arr[i], key=customKey)
print arr
Suppose I have some lookup table, q(w, x, y, z), where various combination of keys map to different values; i.e., q(0, 0, 0, 0) = a, q(0, 0, 0, 1) = b, q(15, 16, 23, "b") = c.
What's the best way to implement this structure in Ruby in terms of efficiency? The keys will be generated dynamically and will generally be strings. I can think of three different keying methods with hashes:
Use a string as the key: q["a, b, c, d"] = 0
Use a single array as the key: q[["a", "b", "c", "d"]] = 0
Use a hash of hashes: q["a"]["b"]["c"]["d"] = 0
I'm currently using method 2, and it's a little slower than I would like. These key combinations are generated dynamically—if I were to use a hash that takes a single string, will string concatenation be faster? Should I have started with a hash of hashes in the first place? Will this method take more space in memory?
I would opt for something like your #1: create a single string which will then act as your map key. However ensure that your 'surrogate hash key' will be appropriately unique for various combinations of values. In this case you only have to build a simple string and need a single map.
Generally speaking you want map keys to be as immutable as possible. (A key mutating could mess up the table). Sometimes messy in Ruby since strings are mutable but still a worthwhile goal.