JavaScript: Convert array of objects into hashmap
This could be achieved by calling reduce
on your array of values (ie data
), to obtain the required hash map (where ID
is the key and value is the corresponding LABEL
):
const data = [{ID: 0, LABEL: 'turbo'},{ID: 1, LABEL: 'classic'},{ID: 7, LABEL: 'unknown'}];
const hashMap = data.reduce((result, item) => { return { ...result, [ item.ID ] : item.LABEL };}, {});
const hashMapJson = JSON.stringify(hashMap);
console.log('hashMap', hashMap);console.log('hashMapJson', hashMapJson); /*More concise syntax:console.log(data.reduce((result, { ID, LABEL }) => ({ ...result, [ ID ] : LABEL }), {}))*/
Transform array of objects to a map indexed by object key
To filter the keys based on which have a string value you can't use Extarct
that filters the keys which are strings (not number or symbol). You can use KeyOfType
described here
type KeyOfType<T, V> = keyof {
[P in keyof T as T[P] extends V? P: never]: any
}
function indexArrayByKey<T, K extends KeyOfType<T, string>> (array: T[], key: K): Map<T[K], T> {
return array.reduce((map, obj) => map.set(obj[key], obj), new Map<T[K], T>())
}
Playground Link
We use T[K]
instead of string
because T[K]
could also be a subtype of string
so TS would complain at string
, but for cases this will work well.
Using .map() to iterate over an array of Object values and reassign to new keys
When mapping the values, instead of constructing an object containing a single property, return an entry pair (an array where the first value is the property, and the second value is the associated value) so that you can turn the array of entries into an object with Object.fromEntries
.
const data = React.useMemo(
() =>
props.tableData.map((object) => (
Object.fromEntries(
Object.values(object).map((value, i) => [`col${i + 1}`, value])
)
)),
[props.tableData]
);
This depends on the properties in your input objects being ordered. While this is usually dependable, it's not a good thing to rely on, and won't work if any of the properties become numeric (eg 1
or 5
). Consider if the input object properties could be formatted as an (ordered) array instead.
Convert a List of Object arrays to a List of model Beans grouped by id
To achieve that, we need to group the data by id
and name
.
For that, we can create an intermediate map. The key of this map should contain two peaces of information id
and name
. A quick and dirty way is to concatenate these properties as a string, or wrap them with a list or array of Object
type, which even is a more nasty workaround.
The proper way of doing this with Java 8 would to define a separate class as data carrier for the keys and with Java 16 onwards, we can define it as a record. But since you've said that MyBean
implements equals/hashCode
contract based on the id
property, we can use it as key in the intermediate map.
For convenience, I've added one method parse()
to the MyBean
class (if this change is undesirable, you can extract it away from the class and place this logic into the collector).
public class MyBean {
private Integer id;
private String name;
private Map<String, Object> customAttributes;
public MyBean(Integer id, String name) {
this.id = id;
this.name = name;
}
public static MyBean parse(Object[] args) {
return new MyBean((Integer) args[0], (String) args[1]);
}
// getters, setters, equals/hashCode, toString
}
To generate a map using Stream API we can make use of the collector groupingBy()
. As a classifier
we need to provide function that creates a key - MyBean
based on array Object[]
. And a downstream collector we can use a collector that combines all "custom attributes" mapped to the same key into a map.
Then we can create a stream over the entries of the intermediate map and parse each entry into MyBean
. And finally collect the beans into a list.
That's how it might look like:
public static void main(String[] args) {
List<Object[]> arrays = buildResultList();
List<MyBean> beans = arrays.stream()
.collect(Collectors.groupingBy(
MyBean::parse,
Collector.of(
HashMap::new,
(Map<String, Object> map, Object[] arr) -> map.put((String) arr[2], arr[3]),
(left, right) -> { left.putAll(right); return left; })
))
.entrySet().stream()
.map(entry -> new MyBean(entry.getKey().getId(),
entry.getKey().getName(),
entry.getValue()))
.collect(Collectors.toList());
beans.forEach(System.out::println);
}
And since MyBean
is mutable, take advantage of this by reusing the keys. Note that it's not considered to be a good practice to change the state of the attributes of the lambda expression, for that reason to mutate the key of the map a plain I'm using an enhanced for
loop.
public static void main(String[] args) {
List<Object[]> arrays = buildResultList();
Map<MyBean, Map<String, Object>> attributesByBean = arrays.stream()
.collect(Collectors.groupingBy(
MyBean::parse,
Collector.of(
HashMap::new,
(Map<String, Object> map, Object[] arr) -> map.put((String) arr[2], arr[3]),
(left, right) -> { left.putAll(right); return left; })
));
List<MyBean> beans = new ArrayList<>();
for (Map.Entry<MyBean, Map<String, Object>> entry : attributesByBean.entrySet()) {
MyBean k = entry.getKey();
k.setCustomAttributes(entry.getValue());
beans.add(k);
}
beans.forEach(System.out::println);
}
Output:
MyBean{id=3, name='name 3', customAttributes={custom_attr_2=custom_attr_2_val_3}}
MyBean{id=1, name='name 1', customAttributes={custom_attr_2=custom_attr_2_val_1}}
MyBean{id=3, name='name 3', customAttributes={custom_attr_3=custom_attr_3_val_3}}
MyBean{id=3, name='name 3', customAttributes={custom_attr_1=custom_attr_1_val_3}}
MyBean{id=2, name='name 2', customAttributes={custom_attr_3=custom_attr_3_val_2}}
MyBean{id=1, name='name 1', customAttributes={custom_attr_3=custom_attr_3_val_1}}
MyBean{id=2, name='name 2', customAttributes={custom_attr_1=custom_attr_1_val_2}}
MyBean{id=1, name='name 1', customAttributes={custom_attr_1=custom_attr_1_val_1}}
MyBean{id=2, name='name 2', customAttributes={custom_attr_2=custom_attr_2_val_2}}
Convert an array of objects to a hash based on a property of the object
JavaScript has Map
for maps, so you could do this:
const data = [{"property1": "x","property2": "value1","property3": "value1"},{"property1": "x","property2": "value2","property3": "value2"},{"property1": "y","property2": "value3","property3": "value3"},{"property1": "y","property2": "value4","property3": "value4"}];
const map = new Map(data.map(o => [o.property1, []]));data.forEach(({property1, ...rest}) => map.get(property1).push(rest));const result = Object.fromEntries(map);
console.log(result);
Object values to map
I'm setting the name as a key using "[]" and returning a object with the height
const obj = [
{ id: 1, height: 57, name: 'jhon', state: 'true', time: '' },
{ id: 2, height: 64, name: 'max', state: 'true', time: '' },
{ id: 3, height: 62, name: 'tim', state: 'false', time: '' },
{ id: 4, height: 510, name: 'alex', state: 'false', time: '' },
{ id: 5, height: 510, name: 'frank', state: 'true', time: '' }
]
const result = obj.map(user => {
return {[user.name]: user.height}
})
console.log(result);
Not able to inject Map into the object while testing with Mockito
Only reflection can help with private static field:
@ExtendWith(MockitoExtension.class)
public class FunTest {
private Fun classUndertest = new Fun();
@Test
public void testfoo() throws NoSuchFieldException {
Map<String, Long> test = Map.of("test", 2L);
FieldSetter.setField(classUndertest, Fun.class.getDeclaredField("map1"), test);
long value = classUndertest.foo("test");
Assertions.assertEquals(2L, value);
}
}
Javascript Convert an array to map keyed with the array property and store the corresponding duplicate key values as array
You can use reduce
like this:
Check if the accumulator
already has key with current a.Record.Account
. If yes, push the current item in context to it. Else, add a.Record.Account
as a key and then push the item to it.
const input = [{'Key':'1','Record':{'Account':'a','data':'A1'}},{'Key':'2','Record':{'Account':'b','data':'123'}},{'Key':'3','Record':{'Account':'a','data':'A2'}},{'Key':'4','Record':{'Account':'a','data':'A3'}},{'Key':'5','Record':{'Account':'c','data':'123'}}]
const output = input.reduce((acc, a) => ((acc[a.Record.Account] = acc[a.Record.Account] || []).push(a), acc), {})console.log(output);
Related Topics
How to Remove Element from an Array in JavaScript
Calling Setstate in a Loop Only Updates State 1 Time
Is a JavaScript Array Index a String or an Integer
Var Name Produces Strange Result in JavaScript
Throwing Strings Instead of Errors
Fastest Way to Check a String Contain Another Substring in JavaScript
Sum of Array Object Property Values in New Array of Objects in JavaScript
Js String.Split() Without Removing the Delimiters
Node.Js Tail-Call Optimization: Possible or Not
How to Print a Stack Trace in Node.Js
Normalizing Mousewheel Speed Across Browsers
Pass Arguments with Page.Evaluate
How to Get Url Parameters with JavaScript
Where to Save a Jwt in a Browser-Based Application and How to Use It