Creating a human-readable list from the values of a map
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
2
down vote
favorite
I'm creating a human-readable list from the keys of a map
object. I want it to be comma-separated, but include the word "and" at the end before the last element, supplying a given locale for the word "and".
E.g., my map might have the strings:
"AK", "Alaska"
"CA", "California"
"NY", "New York"
I would want as output:
Alaska, California, and New York
for English and
Alaska, California, y New York
for Spanish.
So I have:
public static String getHumanReadableStates(Map<String, String> stateMap)
long count = stateMap.values().stream().map(Object::toString).count();
return stateMap.values().stream().map(Object::toString)
.limit(count - 1)
.collect(Collectors.joining(", "))
.concat(", ")
.concat(LocaleUtil.getBundleValue("theWordAnd"))
.concat(" ")
.concat(Objects.requireNonNull(stateMap.values().stream().reduce((first, second) -> second)
.orElse(null)));
where LocaleUtil
has a method called getBundleValue()
to simply get the word "and" from messages.properties
.
Is there a simpler way to achieve this? Or should streams be avoided altogether?
java
add a comment |Â
up vote
2
down vote
favorite
I'm creating a human-readable list from the keys of a map
object. I want it to be comma-separated, but include the word "and" at the end before the last element, supplying a given locale for the word "and".
E.g., my map might have the strings:
"AK", "Alaska"
"CA", "California"
"NY", "New York"
I would want as output:
Alaska, California, and New York
for English and
Alaska, California, y New York
for Spanish.
So I have:
public static String getHumanReadableStates(Map<String, String> stateMap)
long count = stateMap.values().stream().map(Object::toString).count();
return stateMap.values().stream().map(Object::toString)
.limit(count - 1)
.collect(Collectors.joining(", "))
.concat(", ")
.concat(LocaleUtil.getBundleValue("theWordAnd"))
.concat(" ")
.concat(Objects.requireNonNull(stateMap.values().stream().reduce((first, second) -> second)
.orElse(null)));
where LocaleUtil
has a method called getBundleValue()
to simply get the word "and" from messages.properties
.
Is there a simpler way to achieve this? Or should streams be avoided altogether?
java
Probably belongs on SO or se.SE though?
â Will Crawford
Jan 6 at 6:21
Yeah, I actually posted to SO, but deleted and thought it may actually fit better here.
â bphilipnyc
Jan 6 at 19:32
add a comment |Â
up vote
2
down vote
favorite
up vote
2
down vote
favorite
I'm creating a human-readable list from the keys of a map
object. I want it to be comma-separated, but include the word "and" at the end before the last element, supplying a given locale for the word "and".
E.g., my map might have the strings:
"AK", "Alaska"
"CA", "California"
"NY", "New York"
I would want as output:
Alaska, California, and New York
for English and
Alaska, California, y New York
for Spanish.
So I have:
public static String getHumanReadableStates(Map<String, String> stateMap)
long count = stateMap.values().stream().map(Object::toString).count();
return stateMap.values().stream().map(Object::toString)
.limit(count - 1)
.collect(Collectors.joining(", "))
.concat(", ")
.concat(LocaleUtil.getBundleValue("theWordAnd"))
.concat(" ")
.concat(Objects.requireNonNull(stateMap.values().stream().reduce((first, second) -> second)
.orElse(null)));
where LocaleUtil
has a method called getBundleValue()
to simply get the word "and" from messages.properties
.
Is there a simpler way to achieve this? Or should streams be avoided altogether?
java
I'm creating a human-readable list from the keys of a map
object. I want it to be comma-separated, but include the word "and" at the end before the last element, supplying a given locale for the word "and".
E.g., my map might have the strings:
"AK", "Alaska"
"CA", "California"
"NY", "New York"
I would want as output:
Alaska, California, and New York
for English and
Alaska, California, y New York
for Spanish.
So I have:
public static String getHumanReadableStates(Map<String, String> stateMap)
long count = stateMap.values().stream().map(Object::toString).count();
return stateMap.values().stream().map(Object::toString)
.limit(count - 1)
.collect(Collectors.joining(", "))
.concat(", ")
.concat(LocaleUtil.getBundleValue("theWordAnd"))
.concat(" ")
.concat(Objects.requireNonNull(stateMap.values().stream().reduce((first, second) -> second)
.orElse(null)));
where LocaleUtil
has a method called getBundleValue()
to simply get the word "and" from messages.properties
.
Is there a simpler way to achieve this? Or should streams be avoided altogether?
java
edited Jan 6 at 3:39
asked Jan 6 at 3:33
bphilipnyc
137119
137119
Probably belongs on SO or se.SE though?
â Will Crawford
Jan 6 at 6:21
Yeah, I actually posted to SO, but deleted and thought it may actually fit better here.
â bphilipnyc
Jan 6 at 19:32
add a comment |Â
Probably belongs on SO or se.SE though?
â Will Crawford
Jan 6 at 6:21
Yeah, I actually posted to SO, but deleted and thought it may actually fit better here.
â bphilipnyc
Jan 6 at 19:32
Probably belongs on SO or se.SE though?
â Will Crawford
Jan 6 at 6:21
Probably belongs on SO or se.SE though?
â Will Crawford
Jan 6 at 6:21
Yeah, I actually posted to SO, but deleted and thought it may actually fit better here.
â bphilipnyc
Jan 6 at 19:32
Yeah, I actually posted to SO, but deleted and thought it may actually fit better here.
â bphilipnyc
Jan 6 at 19:32
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
2
down vote
accepted
A stream-based approach is still feasible, by materializing your state names into a List
first.
By using a List
, you can retrieve all the names except the last using List.subList(int, int)
(this may be empty, for a single-entry Map
), and then retrieving the last (or the only, for a single-entry Map
) directly using a simple List.get(int)
. Afterwards, you just need to handle for an empty Map
or String
values.
public static String getHumanReadableStates(Map<String, String> stateMap)
List<String> names = new ArrayList<>(stateMap.values());
if (names.isEmpty())
return "";
String exceptLastPlusBlank = Stream.concat(
names.subList(0, names.size() - 1).stream(), Stream.of(""))
.collect(Collectors.joining(", "));
return Stream.of(exceptLastPlusBlank, names.get(names.size() - 1))
.filter(v -> !v.isEmpty())
.collect(Collectors.joining(LocaleUtil.getBundleValue("theWordAnd") + " "));
Stream.of("")
is introduced to append the final comma.
add a comment |Â
up vote
1
down vote
To get the different behaviour for the final element, you will need to be able to tell how many items are left in the list before you hit the end, so that seems to rule out streams, since you only have access to one element at a time, and the "built in" joining
collector doesn't appear to allow any special treatment for the final element.
So really, you're reduced to StringBuilder
and the logic (pseudocode):
- if (have any elements at all)
- add first element to string
- while (have more than one element remaining)
- add delimiter (", " I guess), then the next element from the list
- add " and " (or translation), then the final element from the list
Or, for slightly more readable code [citation needed], still using streams a bit, take the list of values, and:
- if (have any elements at all)
- remove final element and keep safe somewhere
- if (any elements remaining)
- apply
.stream().collect(Collectors.joining(", "))
to list - concatenate with " and " (or translation) and the saved final element, then return it
- else, just return the saved final element
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
A stream-based approach is still feasible, by materializing your state names into a List
first.
By using a List
, you can retrieve all the names except the last using List.subList(int, int)
(this may be empty, for a single-entry Map
), and then retrieving the last (or the only, for a single-entry Map
) directly using a simple List.get(int)
. Afterwards, you just need to handle for an empty Map
or String
values.
public static String getHumanReadableStates(Map<String, String> stateMap)
List<String> names = new ArrayList<>(stateMap.values());
if (names.isEmpty())
return "";
String exceptLastPlusBlank = Stream.concat(
names.subList(0, names.size() - 1).stream(), Stream.of(""))
.collect(Collectors.joining(", "));
return Stream.of(exceptLastPlusBlank, names.get(names.size() - 1))
.filter(v -> !v.isEmpty())
.collect(Collectors.joining(LocaleUtil.getBundleValue("theWordAnd") + " "));
Stream.of("")
is introduced to append the final comma.
add a comment |Â
up vote
2
down vote
accepted
A stream-based approach is still feasible, by materializing your state names into a List
first.
By using a List
, you can retrieve all the names except the last using List.subList(int, int)
(this may be empty, for a single-entry Map
), and then retrieving the last (or the only, for a single-entry Map
) directly using a simple List.get(int)
. Afterwards, you just need to handle for an empty Map
or String
values.
public static String getHumanReadableStates(Map<String, String> stateMap)
List<String> names = new ArrayList<>(stateMap.values());
if (names.isEmpty())
return "";
String exceptLastPlusBlank = Stream.concat(
names.subList(0, names.size() - 1).stream(), Stream.of(""))
.collect(Collectors.joining(", "));
return Stream.of(exceptLastPlusBlank, names.get(names.size() - 1))
.filter(v -> !v.isEmpty())
.collect(Collectors.joining(LocaleUtil.getBundleValue("theWordAnd") + " "));
Stream.of("")
is introduced to append the final comma.
add a comment |Â
up vote
2
down vote
accepted
up vote
2
down vote
accepted
A stream-based approach is still feasible, by materializing your state names into a List
first.
By using a List
, you can retrieve all the names except the last using List.subList(int, int)
(this may be empty, for a single-entry Map
), and then retrieving the last (or the only, for a single-entry Map
) directly using a simple List.get(int)
. Afterwards, you just need to handle for an empty Map
or String
values.
public static String getHumanReadableStates(Map<String, String> stateMap)
List<String> names = new ArrayList<>(stateMap.values());
if (names.isEmpty())
return "";
String exceptLastPlusBlank = Stream.concat(
names.subList(0, names.size() - 1).stream(), Stream.of(""))
.collect(Collectors.joining(", "));
return Stream.of(exceptLastPlusBlank, names.get(names.size() - 1))
.filter(v -> !v.isEmpty())
.collect(Collectors.joining(LocaleUtil.getBundleValue("theWordAnd") + " "));
Stream.of("")
is introduced to append the final comma.
A stream-based approach is still feasible, by materializing your state names into a List
first.
By using a List
, you can retrieve all the names except the last using List.subList(int, int)
(this may be empty, for a single-entry Map
), and then retrieving the last (or the only, for a single-entry Map
) directly using a simple List.get(int)
. Afterwards, you just need to handle for an empty Map
or String
values.
public static String getHumanReadableStates(Map<String, String> stateMap)
List<String> names = new ArrayList<>(stateMap.values());
if (names.isEmpty())
return "";
String exceptLastPlusBlank = Stream.concat(
names.subList(0, names.size() - 1).stream(), Stream.of(""))
.collect(Collectors.joining(", "));
return Stream.of(exceptLastPlusBlank, names.get(names.size() - 1))
.filter(v -> !v.isEmpty())
.collect(Collectors.joining(LocaleUtil.getBundleValue("theWordAnd") + " "));
Stream.of("")
is introduced to append the final comma.
answered Jan 6 at 13:42
h.j.k.
18.1k32490
18.1k32490
add a comment |Â
add a comment |Â
up vote
1
down vote
To get the different behaviour for the final element, you will need to be able to tell how many items are left in the list before you hit the end, so that seems to rule out streams, since you only have access to one element at a time, and the "built in" joining
collector doesn't appear to allow any special treatment for the final element.
So really, you're reduced to StringBuilder
and the logic (pseudocode):
- if (have any elements at all)
- add first element to string
- while (have more than one element remaining)
- add delimiter (", " I guess), then the next element from the list
- add " and " (or translation), then the final element from the list
Or, for slightly more readable code [citation needed], still using streams a bit, take the list of values, and:
- if (have any elements at all)
- remove final element and keep safe somewhere
- if (any elements remaining)
- apply
.stream().collect(Collectors.joining(", "))
to list - concatenate with " and " (or translation) and the saved final element, then return it
- else, just return the saved final element
add a comment |Â
up vote
1
down vote
To get the different behaviour for the final element, you will need to be able to tell how many items are left in the list before you hit the end, so that seems to rule out streams, since you only have access to one element at a time, and the "built in" joining
collector doesn't appear to allow any special treatment for the final element.
So really, you're reduced to StringBuilder
and the logic (pseudocode):
- if (have any elements at all)
- add first element to string
- while (have more than one element remaining)
- add delimiter (", " I guess), then the next element from the list
- add " and " (or translation), then the final element from the list
Or, for slightly more readable code [citation needed], still using streams a bit, take the list of values, and:
- if (have any elements at all)
- remove final element and keep safe somewhere
- if (any elements remaining)
- apply
.stream().collect(Collectors.joining(", "))
to list - concatenate with " and " (or translation) and the saved final element, then return it
- else, just return the saved final element
add a comment |Â
up vote
1
down vote
up vote
1
down vote
To get the different behaviour for the final element, you will need to be able to tell how many items are left in the list before you hit the end, so that seems to rule out streams, since you only have access to one element at a time, and the "built in" joining
collector doesn't appear to allow any special treatment for the final element.
So really, you're reduced to StringBuilder
and the logic (pseudocode):
- if (have any elements at all)
- add first element to string
- while (have more than one element remaining)
- add delimiter (", " I guess), then the next element from the list
- add " and " (or translation), then the final element from the list
Or, for slightly more readable code [citation needed], still using streams a bit, take the list of values, and:
- if (have any elements at all)
- remove final element and keep safe somewhere
- if (any elements remaining)
- apply
.stream().collect(Collectors.joining(", "))
to list - concatenate with " and " (or translation) and the saved final element, then return it
- else, just return the saved final element
To get the different behaviour for the final element, you will need to be able to tell how many items are left in the list before you hit the end, so that seems to rule out streams, since you only have access to one element at a time, and the "built in" joining
collector doesn't appear to allow any special treatment for the final element.
So really, you're reduced to StringBuilder
and the logic (pseudocode):
- if (have any elements at all)
- add first element to string
- while (have more than one element remaining)
- add delimiter (", " I guess), then the next element from the list
- add " and " (or translation), then the final element from the list
Or, for slightly more readable code [citation needed], still using streams a bit, take the list of values, and:
- if (have any elements at all)
- remove final element and keep safe somewhere
- if (any elements remaining)
- apply
.stream().collect(Collectors.joining(", "))
to list - concatenate with " and " (or translation) and the saved final element, then return it
- else, just return the saved final element
answered Jan 6 at 6:20
Will Crawford
1112
1112
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f184416%2fcreating-a-human-readable-list-from-the-values-of-a-map%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Probably belongs on SO or se.SE though?
â Will Crawford
Jan 6 at 6:21
Yeah, I actually posted to SO, but deleted and thought it may actually fit better here.
â bphilipnyc
Jan 6 at 19:32