What is the equivalent of a Java HashMap<String,Integer> in Swift - java

I have an example written in Java that I would like to convert into Swift. Below is a section of the code. I would really appreciate if you can help.
Map<String, Integer> someProtocol = new HashMap<>();
someProtocol.put("one", Integer.valueOf(1));
someProtocol.put("two", Integer.valueOf(2));
for (Map.Entry<String, Integer> e : someProtocol.entrySet() {
int index = e.getValue();
...
}
NOTE: entrySet() is a method of the java.util.Map interface whereas getValue() is a method of the java.util.Map.Entry interface.

I believe you can use a dictionary. Here are two ways to do the dictionary part.
var someProtocol = [String : Int]()
someProtocol["one"] = 1
someProtocol["two"] = 2
or try this which uses type inference
var someProtocol = [
"one" : 1,
"two" : 2
]
as for the for loop
var index: Int
for (e, value) in someProtocol {
index = value
}

let stringIntMapping = [
"one": 1,
"two": 2,
]
for (word, integer) in stringIntMapping {
//...
print(word, integer)
}

I guess it will be something like that:
let someProtocol = [
"one" : 1,
"two" : 2
]
for (key, value) in someProtocol {
var index = value
}

Related

What's the most efficient way to combine objects in a List?

Let's say I have the following list.
List<StringInteger> test = new ArrayList<>(); //StringInteger is just a pojo with String and int
test.add(new StringInteger("a", 1));
test.add(new StringInteger("b", 1));
test.add(new StringInteger("a", 3));
test.add(new StringInteger("c", 1));
test.add(new StringInteger("a", 1));
test.add(new StringInteger("c", -1));
System.out.println(test); // [{ a : 1 }, { b : 1 }, { a : 3 }, { c : 1 }, { a : 1 }, { c : -1 }]
I need to write a method that would unite items by String key and add integers. So that the result list would be [{ a : 5 }, { b : 1 }, { c : 0 }]
I could do it using HashMap, but if I go that way - I'll have to create a Map, then use enhanced for-loop with if(containsKey(...)) and then convert it back to List. It just seems like an overkill.
Is there a more elegant solution? I thought that flatMap from Stream API should do the thing, but I cannot figure out how.
Here's my clumsy solution. It works, but I believe that it can be done more simple than that.
Map<String, Integer> map = new HashMap<>();
for (StringInteger stringInteger : test) {
if (map.containsKey(stringInteger.getKey())) {
int previousValue = map.get(stringInteger.getKey());
map.put(stringInteger.getKey(), previousValue + stringInteger.getValue());
} else {
map.put(stringInteger.getKey(), stringInteger.getValue());
}
}
List<StringInteger> result = map.entrySet()
.stream()
.map(stringIntegerEntry -> new StringInteger(stringIntegerEntry.getKey(), stringIntegerEntry.getValue()))
.collect(Collectors.toList());
System.out.println(result); // [{ a : 5 }, { b : 1 }, { c : 0 }]
The simplest way to accomplish this is likely
List<StringInteger> combined = test.stream()
.collect(
Collectors.groupingBy(
StringInteger::getKey,
Collectors.summingInt(StringInteger::getValue)))
.entrySet()
.stream()
.map(entry -> new StringInteger(entry.getKey(), entry.getValue()))
.toList();
Here is a full example with code based on the code seen in two good Answers by Ivanchenko and by Wasserman.
Here, we use a record in Java 16+ to define your StringInt class.
The name StringInt is used rather than StringInteger, to stress that we have a primitive int as the member field type rather than Integer class as the type.
I should think a Map < String , Integer > would suffice for your goal.
record StringInt( String string , int integer ) { }
List < StringInt > inputs =
List.of(
new StringInt( "a" , 1 ) ,
new StringInt( "b" , 1 ) ,
new StringInt( "a" , 3 ) ,
new StringInt( "c" , 1 ) ,
new StringInt( "a" , 1 ) ,
new StringInt( "c" , - 1 )
);
Map < String, Integer > results =
inputs
.stream()
.collect(
Collectors.groupingBy(
StringInt :: string , // Key
Collectors.summingInt( StringInt :: integer ) // Value
) );
results = {a=5, b=1, c=0}
Or, if you insist on instantiating StringInt objects as the result:
record StringInt( String string , int integer ) { }
List < StringInt > inputs =
List.of(
new StringInt( "a" , 1 ) ,
new StringInt( "b" , 1 ) ,
new StringInt( "a" , 3 ) ,
new StringInt( "c" , 1 ) ,
new StringInt( "a" , 1 ) ,
new StringInt( "c" , - 1 )
);
List < StringInt > results =
inputs
.stream()
.collect(
Collectors.groupingBy(
StringInt :: string , // Key
Collectors.summingInt( StringInt :: integer ) // Value
) )
.entrySet() // Returns a Set < Entry < String , Integer > >
.stream()
.map(
entry -> new StringInt( entry.getKey() , entry.getValue() )
)
.toList();
As #LouisWasserman said in the comment, HashMap is the right tool for this task.
To translate the whole code into a stream, you can use the built-in collector groupingBy() in conjunction with summingInt as the downstream collector grouping.
result = test.stream()
.collect(Collectors.groupingBy( // creates an intermediate map Map<String, Integer>
StringInteger::getKey, // mapping a key
Collectors.summingInt(StringInteger::getValue) // generating a value
))
.entrySet().stream()
.map(entry -> new StringInteger(entry.getKey(), entry.getValue()))
.toList();
Here's my clumsy solution. It works, but I believe that it can be done more simple than that.
Just to show you, you can do the adding up in the map more neatly, without using streams:
for (StringInteger stringInteger : test) {
map.merge(stringInteger.getKey(), stringInteger.getValue(), Integer::sum);
}

Passing JSON keys as String containing a range of numbers

I am trying to have a set of keys to point to a value.
For example the key 0 is to point to the value "EXAMPLE_1"
keys 1,2,3,4,5 is to point to the value "EXAMPLE_2"
and keys 6,7,8,9,10 is to point to the value "EXAMPLE_3"
This is the JSON structure I came up with (which will exist in an external file).
{
"0" : "EXAMPLE_1",
"1,5" : "EXAMPLE_2",
"6,10" : "EXAMPLE_3"
}
Using following code to read and fetch correct value.
private String getValue(String count){
Map<String, String> map = // code to fetch data from the file and get above map. Works.
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
String[] keysInRange = key.split(",");
if(Arrays.asList(keysInRange).contains(count)){
return value;
}
}
}
This technically works but is there a better way to do this.
Looking to improve the JSON structure.
Finding it silly to be passing in the keys in this manner.
Note that the keys would be a single number or always in a range.
You could try below. This is assuming, Keys in range are like this 1,2,3,4,5 for 1,5
private String getValue(String count){
Map<String, String> map = // code to fetch data from the file and get above map. Works.
If(map.containsKey(count)){
return map.get(count);
}
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if(key.contains(","+count) || key.contains(","+count+",") || key.contains(count+",") ){
return value;
}
}
}
You could change your JSON structure to an array of elements:
{[ {
"name": "EXAMPLE_1",
"from": "0",
"to": "0"
},
{
"name": "EXAMPLE_2",
"from": "1",
"to": "5"
},
{
"name": "EXAMPLE_3",
"from": "6",
"to": "10"
}
]}
and parse them with a JSON parser like Jackson oder GSON in data objects like
class Example {
private String name;
private int from;
private int to;
// ommitted getters & setters for brevity
}
Your method then becomes (using Java 8+ and the streams api):
private String getValue(int count) {
Set<Example> examples = ... // code to fetch data from the file
Optional<Example> match = examples.stream()
.filter(example -> example.getFrom() >= count)
.filter(example -> example.getTo() <= count)
.findFirst();
// or return the Optional<Example> directly
return match.map(Example::getValue).orElse(null);
}

Sorting Map<String, List<Object>> using java 8

How can I sort the map using the data from a value
Example: Map<String, List<someObject>> is my input. I need to sort and get the order in someObject.getVar2()
Example:
class SomeObject{
String var1;
String var2;
String var3;
// getters and setters
}
INPUT:
Entry 1:
{
Key1 = 123
[
someObject
[
var1=4
var2=5
var3=6
]
]
}
Entry 2:
{
Key1 = 456
[
someObject
[
var1=2
var2=8
var3=1
]
]
}
Entry 3:
{
Key1 = 789
[
someObject
[
var1=1
var2=2
var3=3
]
]
}
OUTPUT after sorting according to var2:
Entry 1:
{
Key1 = 789
[
someObject
[
var1=1
var2=2
var3=3
]
]
}
Entry 2:
{
Key1 = 123
[
someObject
[
var1=4
var2=5
var3=6
]
]
}
Entry 3:
{
Key1 = 456
[
someObject
[
var1=2
var2=8
var3=1
]
]
}
There are 3 entries in Map with format Map<String, List<someObject>>
I need to sort this Map collection with var2 values
Before keys are in: 123,456,789
After sorting keys are in: 789,123,456
What you want is not possible. Looking at the documentation of the Map interface reveals that
[...] Some map implementations, like the TreeMap class, make specific guarantees as to their order; others, like the HashMap class, do not. [...]
Your input doesn't really fit in the data structure you've used, i.e. Map<String, List<SomeObject>>. Since there is never a List there in the values (and if there be, the logic for sorting would have to be redefined.) Hence you could simply represent your current input as a Map<String, SomeObject>:
Map<String, SomeObject> input = new HashMap<>(); // initialised with your sample
and then sort it as:
Map<String, SomeObject> output = map.entrySet()
.stream()
.sorted(Comparator.comparing(o -> o.getValue().getVar2())) // **
.collect(Collectors
.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new));
** do note that representing Numbers(mostly Integers here) and then sorting based on them is not probably one should desire.

How to group List of Lists using a certain criteria in Java 8

I have a data structure like below. I'm trying to group the objects in such a way like Map<String, List<String>> where key is the entryId and value is the List of groups it belongs to. entryId is always unique inside a group.
Example: entryId "1111" belongs to group1,group2,group3. I'm using the old java 7 way to iterate through the lists and checking. Is there any best possible way using Java8 Collectors/grouping to achieve this.
List<Group> where each Group object will have a list of Entry objects.
[
{
"id":"group1",
"entries":[
{
"entryId":"1111",
"name":"test1"
},
{
"entryId":"2222",
"name":"test2"
},
{
"entryId":"3333",
"name":"test3"
}
]
},
{
"id":"group2",
"entries":[
{
"entryId":"4444",
"name":"test1"
},
{
"entryId":"1111",
"name":"test2"
},
{
"entryId":"2222",
"name":"test3"
}
]
},
{
"id":"group3",
"entries":[
{
"entryId":"1111",
"name":"test1"
},
{
"entryId":"5555",
"name":"test2"
},
{
"entryId":"3333",
"name":"test3"
}
]
}
]
So the expected out put is this :
[
{
"1111":[
"group1",
"group2",
"group3"
]
},
{
"2222":[
"group1",
"group2"
]
},
{
"3333":[
"group1",
"group3"
]
},
{
"4444":[
"group2"
]
},
{
"5555":[
"group3"
]
}
]
I'm using below way currently. which is working as expected, but is there a much simpler way in Java 8 I can achieve this.
public Map<String, List<String>> mapEntries(List<Group> groups) {
Map<String, List<String>> entryMaps = new HashMap<>();
for (Group group : groups) {
for (Entry entry : group.getEntries()) {
List<String> groupsEntryBelongs = new ArrayList<>();
if (groups.iterator().hasNext() && !entryMaps.keySet().contains(entry.getEntryId())) {
updateGroups(groups, entry.getEntryId(), groupsEntryBelongs, entryMaps);
}
}
}
return entryMaps;
}
void updateGroups(List<Group> groups, String id, List<String> groupsEntryBelongs, Map<String, List<String>> entryMaps) {
for (Group group : groups) {
for (Entry entry : group.getEntries()) {
if (entry.getEntryId().equalsIgnoreCase(id)) {
groupsEntryBelongs.add(group.getId());
}
}
}
entryMaps.put(id, groupsEntryBelongs);
}
You can do it as follows:
Map<String, Set<String>> entryMaps = new LinkedHashMap<>();
groups.forEach(group ->
group.getEntries().forEach(entry ->
entryMaps.computeIfAbsent(
entry.getEntryId().toLowerCase(),
k -> new LinkedHashSet<>())
.add(group.getId())));
This iterates the groups, then each group's entries and uses Map.computeIfAbsent to put an entry with a new, empty LinkedHashSet if the key wasn't present, returning either this empty set or the one matching that key. Then, the group id is added to this returned set.
Note: I'm using a Set instead of a List for values, to avoid possible duplicates. And LinkedHashMap and LinkedhashSet guarantee insertion-order.
Something like this ought to work, it requires making some sort of intermediate tuple object:
groups.stream()
.flatMap(group -> group.getEntries().stream()
.map(entry -> Map.entry(entry.getEntryId(), group.getId())))
.collect(Colectors.groupingBy(Map.Entry::getKey,
Colectors.mapping(Map.Entry::getValue, toList())));
List<List<EmployeeHourLogDetails>> resultLisSalesman = new ArrayList<>
(employeeHourLogHeader.getEmployeeHourLogDetails().stream()
.collect(Collectors.groupingBy(d ->
d.getEmployeeId())).values());

Merge maps with recursive nested maps in Groovy

I would like to know if someone have an easy way to merge 2 deep nested maps together ?
For instance, I would like to get :
[
"a" : "1",
"animals" : ["cat" : "blue"]
] + [
"b" : 2,
"animals" : ["dog" : "red"]
] == [
"a" : 1,
"b" : 2,
"animals" : [
"cat" : "blue",
"dog" : "red"]
]
There is someone having easy solution ?
You can write one for Map using recursion:
Map.metaClass.addNested = { Map rhs ->
def lhs = delegate
rhs.each { k, v -> lhs[k] = lhs[k] in Map ? lhs[k].addNested(v) : v }
lhs
}
def map1 = [
"a" : "1",
"animals" : ["cat" : "blue"]
]
def map2 = [
"b" : 2,
"animals" : ["dog" : "red"]
]
assert map1.addNested( map2 ) == [
a: '1',
animals: [cat: 'blue', dog: 'red'],
b: 2
]
I had a similar solution as #dmahapatro, but with a method with variable arguments:
def m1 = [a: 1, animals: [cat: 'blue']]
def m2 = [b: 2, animals: [dog: 'red']]
Map merge(Map... maps) {
Map result
if (maps.length == 0) {
result = [:]
} else if (maps.length == 1) {
result = maps[0]
} else {
result = [:]
maps.each { map ->
map.each { k, v ->
result[k] = result[k] instanceof Map ? merge(result[k], v) : v
}
}
}
result
}
assert [:] == merge()
assert m1 == merge(m1)
assert [a:1, b:2, animals:[cat:'blue', dog:'red']] == merge(m1, m2)
This is a very short, concise way of doing it without using the meta-programming:
Map one = ["a" : "1", "animals" : ["cat" : "blue"] ]
Map two = ["b" : "2", "animals" : ["dog" : "red"] ]
Map three = [:]
(one.entrySet() + two.entrySet()).each { entry ->
three[entry.key] = three.containsKey(entry.key) ? [:] << three[entry.key] << entry.value : entry.value
}
println three
Renders the output:
[a:1, animals:[cat:blue, dog:red], b:2]
Another solution similar to #dmahapatro, but without using metaClass:
Map mergeMaps(Map lhs, Map rhs) {
rhs.each { k, v ->
lhs[k] = (lhs[k] in Map ? mergeMaps(lhs[k], v) : v)
}
return lhs
}
def m1 = [a: 1, animals: [cat: 'blue']]
def m2 = [b: 2, animals: [dog: 'red']]
mergeMaps(m1, m2)
println(m1)

Categories