neyrah
neyrah

Reputation: 177

Find a value by the key in a List of Maps

I have a list of maps List<Map<String,String>> input.

Which can be represented in the following manner:

[{AddressField=AddressUsageType, AddressValue=PRINCIPAL},
 {AddressField=StreetNumber, AddressValue=2020},
 {AddressField=StreetName, AddressValue=Some street}]

I would like to get the AddressValue for a particular AddressField.

For example, I want to get the value "PRINCIPAL" for the key "AddressUsageType".

I have tried using filters and many other MAP functions, but couldn't end up with a proper solution.

This is my code snippet that gets the value of 1st key-value pair:

DataTable table;
List<Map<String,String>> input= table.asMaps(String.class, String.class);

    String AddressField = input.get(0).get("AddressField");
    String AddressValue = input.get(0).get("AddressValue");
    System.out.println("AddressField " +AddressField);
    System.out.println("AddressValue " +AddressValue);

Here is the output of the above snippet:

AddressField AddressUsageType
AddressValue PRINCIPAL

Upvotes: 0

Views: 2909

Answers (4)

dani-vta
dani-vta

Reputation: 6840

Since in your code you have a List where each element is a Map with a single mapping, you could stream the list and filter for the first map containing the value AddressUsageType.

Your code could be written like this:

Map<String, String> map = myList.stream()
        .filter(m -> m.values().contains("AddressUsageType"))
        .findFirst()
        .orElse(null);

if (map != null) {
    System.out.println("AddressField " + map.get("AddressField"));
    System.out.println("AddressValue " + map.get("AddressValue"));
}

Here is also a test main at OneCompiler.

Upvotes: 1

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 28978

I would like to get the value of AddressValue from value of AddressField. For eg.,

I want to get the value "PRINCIPAL" from the key value "AddressUsageType"

You are misusing the Map here. Because every map is expected to have a limited, well-defined set of keys, each of which has a particular meaning in your domain. And every map is mean to represent a some kind of address.

It's highly advisable to group these data into a custom object as soon as it has a particular meaning in your domain model.

The advantages are:

  • Ability to utilize different data types for values instead of storing them as String.
  • You don't have to rely on string keys.
  • You gain an advantage of clean self-explanatory code (in case if you would use clear and precise name).

I understand that it might the code that was developed long ago by someone else, or you might think that for now it's easier to store the data this was. But the more you're postponing the refactoring, the more expensive it becomes.

By treating the object as a collection, you might and up with deeply nested collections like map of maps or map of list of maps that will affect maintainability of code.

That how you can approach this task if you would choose to group the data into a domain class.

For sake of simplicity and conciseness, I would use Java 16 records.

public record UserAddress(AddressUsageType addressUsageType, int streetNumber, String streetName) {}

And AddressUsageType will be represented as enum. If I understood correctly there will a limited number of address type, so it's less error-prone to store this data as enum instead of relying on string values, and also gives few extra opportunities.

public enum AddressUsageType { PRINCIPAL, OTHER_TYPE }

And that's how you can extract a particular field from each UserAddress object in a list, by being able to benefit from its type instead of treating them as string:

public static <T> List<T> getAddressValue(List<UserAddress> addresses,
                                          Function<UserAddress, T> keyExtractor) {
    return addresses.stream()
        .map(keyExtractor)
        .toList(); // available with Java 16 onwards
}

The method shown above expects a list of addresses and a function which extracts a particular property from an addresses object.

main() - demo

public static void main(String[] args) {
    List<UserAddress> addresses =
        List.of(new UserAddress(AddressUsageType.PRINCIPAL, 71, "greenStreet"),
                new UserAddress(AddressUsageType.PRINCIPAL, 83, "purpleStreet"),
                new UserAddress(AddressUsageType.OTHER_TYPE, 85, "pinkStreet"));
    
    List<AddressUsageType> addressUsageTypes = getAddressValue(addresses, UserAddress::addressUsageType);
    List<Integer> streetNumbers = getAddressValue(addresses, UserAddress::streetNumber);
    List<String> streetNames = getAddressValue(addresses, UserAddress::streetName);

    System.out.println("AddressUsageTypes:\n" + addressUsageTypes);
    System.out.println("StreetNumbers:\n" + streetNumbers);
    System.out.println("StreetNames:\n" + streetNames);
}

Output

AddressUsageTypes:
[PRINCIPAL, PRINCIPAL, OTHER_TYPE]
StreetNumbers:
[71, 83, 85]
StreetNames:
[greenStreet, purpleStreet, pinkStreet]

Upvotes: 1

XtremeBaumer
XtremeBaumer

Reputation: 6435

Filter the input to retain only maps which contain AddressField=AddressUsageType. Then extract the AddressValue entry using the .map function and gather them in a result list.

public static void main(String[] args) {
    List<Map<String, String>> input = new ArrayList<>();
    Map<String, String> map = new HashMap<>();
    map.put("AddressField", "AddressUsageType");
    map.put("AddressValue", "PRINCIPAL");
    input.add(map);

    map = new HashMap<>();
    map.put("AddressField", "StreetNumber");
    map.put("AddressValue", "2020");
    input.add(map);
    map = new HashMap<>();
    map.put("AddressField", "StreetName");
    map.put("AddressValue", "Some street");
    input.add(map);

    map = new HashMap<>();
    map.put("AddressField", "AddressUsageType");
    map.put("AddressValue", "NOT_PRINCIPAL");
    input.add(map);
    
    List<String> collect = input.stream().filter(e -> e.get("AddressField").equals("AddressUsageType"))
            .map(e -> e.get("AddressValue")).collect(Collectors.toList());
    System.out.println(input);
    System.out.println(collect);
}

The output is

[{AddressField=AddressUsageType, AddressValue=PRINCIPAL}, {AddressField=StreetNumber, AddressValue=2020}, {AddressField=StreetName, AddressValue=Some street}, {AddressField=AddressUsageType, AddressValue=NOT_PRINCIPAL}]

[PRINCIPAL, NOT_PRINCIPAL]

Upvotes: 2

Eskapone
Eskapone

Reputation: 321

Your usage of Maps is a bit odd here because your actual key "AddressUsageType" is a value inside the map and every map is just one pair of key and value stored behind static keys. If you can not change that, you could go with something like this:

String key = "AddressUsageType";
String result = "";

for (Map<String,String> map : input)
{
    if (map.containsValue(key))
    {
        result = map.get("AddressValue");
        break;
    }
}

System.out.println(key + " is " + result);

Upvotes: 2

Related Questions