I am receiving the following array from an API response:
[
{group: '1', , value: 'a'}
{group: '1', , value: 'b'}
{group: '2', , value: 'c'}
{group: '2', , value: 'd'}
]
I want to convert it to the following (want order of groups as received, values can be ordered in any way):
[
{group: '1', values: ['b', 'a'] },
{group: '2', values: ['c', 'd'] },
]
Which Javascript function will be the most efficient to convert it?
I am able to do this by:
let result = [];
data.reduce((groupNumber, input) => {
if (!groupNumber[input.group]) {
groupNumber[input.group] = {group: input.group, values: []};
result.push(groupNumber[input.group]);
}
groupNumber[input.group].values.push(input);
return groupNumber;
}, {});
Is reduce the correct function to be used here? Is there a more efficient approach?
Secondly, will reduce preserve the order in the result? If I am actually receiving the data with group 1 entries first, group 2 next, and so on, can I rely on result being ordered similarly?
Note that I only care about the order of the groups, not about the values inside the group.
Actually either by using reduce() or any other Array methods, the order will be always preserved as they will be executed from index 0 till the end of the array.
But reduce() is mainly used to accumulate the array elements into a single element, it's not the best method for doing such thing but it can be used as well.
Note that your actual code using reduce() isn't returning the right output.
In my opinion I think Array.map() method is better in this case, but it should be used in combination with Array.filter() to remove duplicate elements that will be kept with map():
var result = data.map(v => {
v.values = data.filter(e => e.group == v.group).map(x => x.value);
delete v.value;
return v;
}).filter(x => !x.values.some(e => !e));
Demo:
let data = [{
group: '1',
value: 'a'
}, {
group: '1',
value: 'b'
}, {
group: '2',
value: 'c'
}, {
group: '2',
value: 'd'
}];
var result = data.map(v => {
v.values = data.filter(e => e.group == v.group).map(x => x.value);
delete v.value;
return v;
}).filter(x => !x.values.some(e => !e));
console.log(result);
I would save my reply because we have a great explanation why your approach is great, because you have O(N) computational complexity (Thanks for the great comments to #CertainPerformance) and you iterate your array just one time.
Testing whether reduce method preserve order of object keys:
It looks like reduce method is not preserving order of keys. Here we see that keys are sorted in order how then iterated through source array myArray, however in Chrome browser this behavior is not reproducible(keys are sorted):
var myArray = [{letter: 'c'}, {letter:'e'}, {letter: 'e'}, {letter:'b'}, {letter: 'c'}, {letter:'a'}, {letter:'b'}, {letter:'a'}, {letter: 'd'}, {letter: 'd'}, {letter: 'd'}, {letter: 'd'}];
var myOrderedKeys = myArray.reduce((a, {letter}) => {
a[letter] = a[letter] || letter;
a[letter] = letter;
return a;
}, {})
console.log(myOrderedKeys);
Another example where we have more than one letter:
let notSimpleLetters = [{letter: 'ce'}, {letter:'ec'}, {letter: 'ec'}, {letter:'bw'}, {letter: 'ce'}, {letter:'aw'}, {letter:'ba'}, {letter:'aa'},
{letter: 'df'}, {letter: 'cb'}, {letter: 'dc'}, {letter: 'da'}];
let notSimpleKeys = notSimpleLetters.reduce((a, {letter}) => {
a[letter] = a[letter] || letter;
a[letter] = letter;
return a;
}, {})
console.log(notSimpleKeys);
Related
I have an array of objects where I need to modify and replace values of specific keys:
arr = [ {key: 'a', value: 1}, {key: 'b', value: 2}, {key: 'c', value: 3}, {key: 'd', value: 4}, {key: 'e', value: 5}]
furthest I could figure was to filter arr.filter(i => i.key.includes('b','c','d')).map(i => i.value) but this would stop only on the first included filter and hold only one value (2). Id thought it would hold objects of keys b c d which values id then modify.
const arr = [{
key: 'a',
value: 1
}, {
key: 'b',
value: 2
}, {
key: 'c',
value: 3
}, {
key: 'd',
value: 4
}, {
key: 'e',
value: 5
}]
console.log(arr.filter(i => i.key.includes('b', 'c', 'd')).map(i => i.value))
Just iterate array and check if key of item matches desired keys and if so assign new value.
Because arr is an array of objects and objects are stored by reference you can alter the object directly without creating a new one.
const keys = ["a", "b", "c"];
const newValue = 1;
arr.forEach((item) => {
if (keys.includes(item.key)) {
item.value = newValue;
}
})
hi guys I have some problem when combining two nested parent array of object , I want to combine only children item when having same group parent.
this my case:
i have two array
var arr1 = [{group: "a", items: ["a","b","c","d"]}, {group: "b", items:["k","l","m"]}];
var arr1 = [{group: "b", items: ["o","p","q","r"]}, {group: "c", items:["1","2","3","4"]}]
I want to combine these two array. if the second array has same parent group it will be just pushed of items, there is the expectation of result:
[
{
group: "a",
items: ["a","b","c","d"]
},
{
group: "b",
items: ["k","l","m","o","p","q", "r"]
},
{
group: "c",
items: ["1","2","3","4"]
},
]
in this case, arr1 and arra2 have the same b group. it will union only the item. I have searched everywhere but I still have no idea. please help me. thank you in advance
You can combine two arrays first and using Array.prototype.reduce, group them by group key.
var arr1 = [{group: "a", items: ["a","b","c","d"]}, {group: "b", items:["k","l","m"]}];
var arr2 = [{group: "b", items: ["o","p","q","r"]}, {group: "c", items:["1","2","3","4"]}]
const groupBy = [...arr1, ...arr2].reduce((acc, cur) => {
acc[cur.group] ? acc[cur.group].items = [ ...acc[cur.group].items, ...cur.items ] : acc[cur.group] = cur;
return acc;
}, {});
const output = Object.values(groupBy);
console.log(output);
I have following data structure that I get from some api:
response = [{grade: 'A', frequency: 54, percentage: 24},
{grade: 'B', frequency: 50, percentage: 10},
{grade: 'C', frequency: 0, percentage: 0},
{grade: 'D', frequency: 50, percentage: 20},
...
];
Now some UI javascript library requires this data to be formatted and presented as follows:
label: ['A', 'B', 'C', 'D'];
data: [[54,50,0,50],[24,10,0,20]];
series: ['frequency', 'percentage'];
What would be the most efficient way to convert the response object to the above elements?
let label = response.map(data => data.grade);
let freqencyData = response.map(data => data.freqency);
let percentageData = response.map(data => data.percentage);
let data = [freqencyData, percentageData];
Would something like this this be efficient enough? Please not that this is an example and the response data in my case is too big and having the map done three times seems to be an overkill.
Thanks
You can use the function reduce to build the almost desired output in one loop.
Separate the label attribute with the rest of the attributes.
Group the values by the specific property-name (data, label, and series).
The Object Set allows you to keep unique strings for the series.
Object.from converts the object Set to Array.
Object.values extracts from this: {frequency: [1,2,3], percentage: [1,2,3]} to this: [[1,2,3], [1,2,3]]
let response = [{grade: 'A', frequency: 54, percentage: 24},{grade: 'B', frequency: 50, percentage: 10},{grade: 'C', frequency: 0, percentage: 0},{grade: 'D', frequency: 50, percentage: 20}];
let result = response.reduce((a, {grade: label, ...rest}) => {
Object.keys(rest).forEach(k => {
(a.series || (a.series = new Set())).add(k);
a.data = a.data || {};
(a.data[k] || (a.data[k] = [])).push(rest[k]);
});
(a.label || (a.label = [])).push(label);
return a;
}, Object.create(null));
result.series = Array.from(result.series);
result.data = Object.values(result.data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could just do an old fashioned loop:
let label = [];
let freqencyData = [];
let percentageData = [];
response.forEach(function(element) {
label.push(data.grade);
frequencyData.push(data.frequency);
percentageData.push(data.percentage);
});
let data = [freqencyData, percentageData];
for example I have an object that that has objects and arrays in itself:
const object =
{
a: {
b: [
0: 'something',
1: {
c: 'the thing that I need',
},
],
},
};
and an array that has the keys as values:
const array =
[
'a', 'b', '1', 'c',
];
How can I use this array to navigate in the object and give me the value?
Maybe there is a way to do this with ramda? or just in general to make it look human readable.
You can reduce the array defining the path through the object.
You do have an error in the array. The path should be: [ 'a', 'b', '1', 'c' ], because the thing you need is inside the second element of the b array, not the first.
const object = {
a: {
b: [
'something',
{ c: 'the thing that I need' }
],
},
};
const path = [ 'a', 'b', '1', 'c' ];
const result = path.reduce(( source, next ) => source[ next ], object );
console.log( result );
Ian Hoffman-Hicks' superb crocks library has a function that does exactly that
import propPathOr from 'crocks/helpers/propPathOr'
const getC = propPathOr(null, ['a', 'b', '0', 'c'])
getC({ a: { b: [{ c: 'gotcha!' }] } }) === 'gotcha!' // true
This function is called path in Ramda.
Is there a function that can convert an array ['A', 'B', 'C'] to an object array [{name: 'A'}, {name: 'B'}, {name: 'C'}]?
Or do I need to write a util function? It is no big deal to write one but curious if a well known function is already there.
Thanks
You can use Array.prototype.map(). Array.map is a method that will iterate through each element in an Array and return an output based on the callback. You can find more information on Array.map on the MDN: https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/map
And here is a working example: https://jsbin.com/qawener/edit?js,console
In this example, we take each element of the array and we return an object with {"name": }. This then creates the newArray array that will have [{name: 'A'}, {name: 'B'}, {name: 'C'}].
const originalArray = ["a", "b", "c"];
let newArray = originalArray.map(element => { return {name: element}; });
console.log(newArray);
Map works very well for these types of situations.
const array = ['A', 'B', 'C'];
const myNewArray = array.map(function(map, i) {
const dict = {"name":array[i]}
return dict;
}, {});
console.log(myNewArray)
Beside the given answer, you may use short hand properties for the object.
const
names = ["a", "b", "c"],
result = names.map(name => ({ name }));
console.log(result);