Related
I need to display below options in material UI dropdown
from 5 to 60 dropdown should display 5m,10m,15m,20m,,and so on and for 720 and 1440 it should display 12h and 24h
I am using below code to set state in react
` let timeoption = "5,10,15,20,25,30,40,45,60,720,1440";
setTimeOption(timeoption.split(',').map(option => {
return {
id: option,
label: `${option}m`
}
}));`
for 720,1440 label should display in "h" instead of "m"
let timeoption = "5,10,15,20,25,30,40,45,60,720,1440"; setTimeOption(timeoption.split(',').map(option => { return { id: option, label: ${option}m } }));
You can use a ternary operator in label to assign m or h based on the time value.
let timeoption = "5,10,15,20,25,30,40,45,60,720,1440";
setTimeOption(timeoption.split(',').map(option => ({
id: option,
label: `${option}${option <= 60 ? 'm' : 'h'}`
})));
this is the output produced:
[
{ id: '5', label: '5m' },
{ id: '10', label: '10m' },
{ id: '15', label: '15m' },
{ id: '20', label: '20m' },
{ id: '25', label: '25m' },
{ id: '30', label: '30m' },
{ id: '40', label: '40m' },
{ id: '45', label: '45m' },
{ id: '60', label: '60m' },
{ id: '720', label: '720h' },
{ id: '1440', label: '1440h' }
]
I am looking to restructure an array of objects with lodash.
I've been trying to adapt the many examples found online without any luck. It seems I would have to use a combination of _.map and ._groupBy but I can't really wrap my head around this.
Any help is appreciated!
Initial array:
const entries = [
{
year: '2019',
children: [
{ name: 'red', amount: 1, label: 'color' },
{ name: 'yellow', amount: 20, label: 'color' },
{ name: 'green', amount: 12, label: 'color' },
],
},
{
year: '2020',
children: [
{ name: 'red', amount: 1, label: 'color' },
{ name: 'yellow', amount: 3, label: 'color' },
],
},
]
Restructured array:
[
{
id: 'red',
data: [
{ year: '2019', amount: 1 },
{ year: '2020', amount: 1 },
],
},
{
id: 'yellow',
data: [
{ year: '2019', amount: 20 },
{ year: '2020', amount: 3 },
],
},
{
id: 'green',
data: [
{ year: '2019', amount: 12 },
],
},
]
You could chain the whole operations with flatMap, groupBy and mapping.
const entries = [{ year: '2019', children: [{ name: 'red', amount: 1, label: 'color' }, { name: 'yellow', amount: 20, label: 'color' }, { name: 'green', amount: 12, label: 'color' }] }, { year: '2020', children: [{ name: 'red', amount: 1, label: 'color' }, { name: 'yellow', amount: 3, label: 'color' }] }],
result = _(entries)
.flatMap(({ year, children }) => _.map(children, ({ name: id, amount }) => ({ year, id, amount })))
.groupBy('id')
.map((data, id) => ({ id, data: _.map(data, ({ year, amount }) => ({ year, amount })) }))
.value();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
There are probably quite a few different ways of doing this, however, I find the best approach is:
Flatten the children to one array.
Use _.groupBy to create a map of these entries keyed on name.
Use _.entries to get an array of keys and values for the map.
Finally use _.map to transform these entries into our desired output.
const entries = [
{
year: '2019',
children: [
{ name: 'red', amount: 1, label: 'color' },
{ name: 'yellow', amount: 20, label: 'color' },
{ name: 'green', amount: 12, label: 'color' },
],
},
{
year: '2020',
children: [
{ name: 'red', amount: 1, label: 'color' },
{ name: 'yellow', amount: 3, label: 'color' },
],
},
]
// Step 1
let flattenedChildren = _.flatMap(entries, e => e.children.map(c => { return { ...c, year: e.year } }));
// Step 2
let entryMap = _.groupBy(flattenedChildren , "name");
// Step 3
let mapEntries = _.entries(entryMap);
// Step 4
let result = _.map(mapEntries , ([id, items]) => { return { id, data: items.map(item => _.pick(item, ["amount", "year"]))} });
console.log("Result:", result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
I've an array of objects in which I need to find the object which has the same value as that of the string.
I've tried this and it works.
But, is there a way to optimize it without using map?
Code:
const arr = [{
label: 'A',
options: [{
label: 'abc',
value: 'abc'
},
{
label: 'bcd',
value: 'bcd'
}
]
},
{
label: 'B',
options: [{
label: 'cde',
value: 'cde'
},
{
label: 'def',
value: 'def'
}
]
},
{
label: 'C',
options: [{
label: 'efg',
value: 'efg'
},
{
label: 'fgh',
value: 'fgh'
}
]
}
];
const str = 'cde';
const result = arr.map(obj => obj.options.find(item => item.value === str)).find(val => val !== undefined);
console.log('result', result);
Yes, you don't need or want map followed by find. Just a loop:
let result;
for (const obj of arr) {
result = obj.options.find(({value}) => value === str);
if (result) {
break;
}
}
Live Example:
const arr = [{
label: 'A',
options: [{
label: 'abc',
value: 'abc'
},
{
label: 'bcd',
value: 'bcd'
}
]
},
{
label: 'B',
options: [{
label: 'cde',
value: 'cde'
},
{
label: 'def',
value: 'def'
}
]
},
{
label: 'C',
options: [{
label: 'efg',
value: 'efg'
},
{
label: 'fgh',
value: 'fgh'
}
]
}
];
const str = 'cde';
let result;
for (const obj of arr) {
result = obj.options.find(({value}) => value === str);
if (result) {
break;
}
}
console.log('result', result);
You could take Array#flatMap with an empty array as default value.
The result is an array with matching result.
const
arr = [{ label: 'A', options: [{ label: 'abc', value: 'abc' }, { label: 'bcd', value: 'bcd' }] }, { label: 'B', options: [{ label: 'cde', value: 'cde' }, { label: 'def', value: 'def' } ] }, { label: 'C', options: [{ label: 'efg', value: 'efg' }, { label: 'fgh', value: 'fgh' }] }];
str = 'cde';
result = arr.flatMap(obj => obj.options.find(item => item.value === str) || []);
console.log('result', result);
you don't need to use find inside the map which is O(nk);
You can fetch all the options then flat the array to find the required object.
const arr = [{
label: 'A',
options: [{
label: 'abc',
value: 'abc'
},
{
label: 'bcd',
value: 'bcd'
}
]
},
{
label: 'B',
options: [{
label: 'cde',
value: 'cde'
},
{
label: 'def',
value: 'def'
}
]
},
{
label: 'C',
options: [{
label: 'efg',
value: 'efg'
},
{
label: 'fgh',
value: 'fgh'
}
]
}
];
const str = 'cde';
const result = arr.map(({options}) => options).flat().find(({value}) => value === str)
console.log('result', result);
Depending of your needs, you can do this:
const arr = [{ label: 'A', options: [{ label: 'abc', value: 'abc' }, { label: 'bcd', value: 'bcd' }] }, { label: 'B', options: [{ label: 'cde', value: 'cde' }, { label: 'def', value: 'def' } ] }, { label: 'C', options: [{ label: 'efg', value: 'efg' }, { label: 'fgh', value: 'fgh' }] }];
const re1 = /"value":"cde"/
const testStr = JSON.stringify(arr);
console.log(""+testStr)
console.log(re1.test(testStr)) // exists
const re2 = /"label":"(\w)+","value":"cde"/g
console.log(testStr.match(re2)) // label
find in the underscore library will avoid using map in this scenario:
var myObj = _.find(arr, (obj) => {
return _.find(obj.options, (elt) => elt.value === str);
});
I want to find the common elements of multiple array of objects based on a common property. In addition, if an element appears more than once, I want the resulting array to reflect the number of times it occurs in all the arrays.
I tried the following:
var arr = [
[
{ name: 'kiwi', value: 12 },
{ name: 'apple', value: 5 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'milk', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'orange', value: 11 },
],
[
{ name: 'taco', value: 23 },
{ name: 'pizza', value: 78 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'pie', value: 1 },
{ name: 'cake', value: 3 },
{ name: 'banana', value: 7 },
{ name: 'beef', value: 123 },
{ name: 'lime', value: 72 },
{ name: 'pizza', value: 34 },
],
[
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'pizza', value: 23 },
{ name: 'fish', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'banana', value: 77 },
]
];
function findArraysWithCommonName(arr) {
let arrays = [...arr];
var result = arrays.shift().reduce(function(res, v) {
if (arrays.every(function(a) {
return (a.filter(function(e) {
return e.name === v.name
}).length > 0);
})) res.push(v);
return res;
}, []);
return result;
}
console.log(findArraysWithCommonName(arr))
The result I got is:
[
{name: "apple", value: 5},
{name: "apple", value: 12},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
I expect the output to be:
[
{name: "apple", value: 12},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
or
[
{name: "apple", value: 5},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
One approach would be to build a map that relates an object to it's "count" in the array (ie the number of times that object occours in arr).
This can be done via .reduce() where you serialize each object to a string via JSON.stringify(obj) - this string is a unique encoding of the corresponding object shape and state which is used as the key to identify the objects of this form in the mapping. The key is used to query and update the "count" value of the mapping, for each object encountered in the arr.
Once the mapping has been build, filter mapping entries by those with a "count" value greater than one.
Finally for any filtered entries, deserialize the corresponding keys of those entries via .map() to obtain an array of objects that occoured more that one in the original arr.
This approach could be implemented as:
var arr=[[{name:'kiwi',value:12},{name:'apple',value:5},{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'fish',value:5},{name:'milk',value:5},{name:'banana',value:7},{name:'orange',value:11}],[{name:'taco',value:23},{name:'pizza',value:78},{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'fish',value:5},{name:'pie',value:1},{name:'cake',value:3},{name:'banana',value:7},{name:'beef',value:123},{name:'lime',value:72},{name:'pizza',value:34}],[{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'pizza',value:23},{name:'fish',value:5},{name:'banana',value:7},{name:'banana',value:77}]];
/* Flatten array heirachy */
const flatArr = arr.flat();
/* Obtain a count mapping for each object's occourance in flatArr */
const mapObjectToCount = flatArr.reduce((map, item) => {
const key = JSON.stringify(item);
const count = (map[key] ? map[key] : 0) + 1;
return { ...map, [ key ] : count };
}, {})
/* Get key/value pair of the prior mapping, filter the objects by
those that occour more that one time, and obtain the original object
by parsing the key */
const result = Object.entries(mapObjectToCount)
.filter(([json, count]) => count > 1)
.map(([json]) => JSON.parse(json));
console.log(result)
I'd first transform each subarray into an object indexed by the number of occurences of each name. Then, iterate through each of those sub-objects created, creating a new object whose values are the minimum of the values found on the combined object, for every key.
Lastly, return a .filter of the first array, checking whether the occurence count of the name being iterated over on that object is greater than 0, reducing that count by one when found:
function findArraysWithCommonName(arr) {
const [oneArr, ...rest] = arr;
/* Transform each subarray into, eg:
{
"taco": 1,
"pizza": 4,
"apple": 1,
"fish": 1,
"pie": 1,
...
*/
const countsByName = rest.map(
subarr => subarr.reduce((a, { name }) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {})
);
/* Combine the objects into one that contains only the minimum value for each property, eg:
{
"apple": 1,
"pizza": 3,
"fish": 1,
"banana": 1
}
*/
const combinedCountsByName = countsByName.reduce((a, countObj) => {
Object.entries(countObj).forEach(([key, val]) => {
countObj[key] = Math.min(a[key], val) || 0;
});
return countObj;
});
console.log(combinedCountsByName);
return oneArr.filter(({ name }) => {
if (combinedCountsByName[name] > 0) {
combinedCountsByName[name]--;
return true;
}
});
}
var arr = [
[
{ name: 'kiwi', value: 12 },
{ name: 'apple', value: 5 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'milk', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'orange', value: 11 },
],
[
{ name: 'taco', value: 23 },
{ name: 'pizza', value: 78 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'pie', value: 1 },
{ name: 'cake', value: 3 },
{ name: 'banana', value: 7 },
{ name: 'beef', value: 123 },
{ name: 'lime', value: 72 },
{ name: 'pizza', value: 34 },
],
[
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'pizza', value: 23 },
{ name: 'fish', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'banana', value: 77 },
]
];
console.log(findArraysWithCommonName(arr));
I am trying to group similar objects with the same label.
At the moment, this is the the JSON I receive.
const sizes = [{
id: [{
value: '2496',
label: 'XS'
}, {
value: '2499',
label: 'S'
}],
type: 'First Size'
}, {
id: [{
value: '2863',
label: 34
}, {
value: '2866',
label: 36
}],
type: 'Shoe Sizes'
}, {
id: [{
value: '3561',
label: 'XS'
}, {
value: '3563',
label: 'S'
}, {
value: '3565',
label: 'L'
}, , {
value: '3567',
label: 'XL'
}]
}, {
id: [{
value: '3523',
label: 34
}, {
value: '2866',
label: 36
}],
type: 'Shoe Sizes'
}]
The result I am trying to achieve is
const sizes = [{
id: [{
value: '2496,3561',
label: 'XS'
}, {
value: '2499,3563',
label: 'S'
}],
type: 'First Size'
}, {
id: [{
value: '2863,3523',
label: 34
}, {
value: '2866',
label: 36
}],
type: 'Shoe Sizes'
}, {
id: [{
value: '3565',
label: 'L'
}, , {
value: '3567',
label: 'XL'
}]
}, {
id: [{
value: '2866',
label: 37
}],
type: 'Shoe Sizes'
}]
I have tried to achieve this with underscore, but I am only able to group it by just one label, and I need to group it by any kind of label, whether it be XS or 36.
I have tried with reduce below, it is close but I just need to remove the brackets around the value, and turn the value into a string.
EX: value: '2493, 2343'
var group_to_values = sizes.reduce(function (obj, item) {
obj[item.label] = obj[item.label] || [];
obj[item.label].push(item.value);
return obj;
}, {});
var groups = Object.keys(group_to_values).map(function (key) {
return {label: key, value: group_to_values[key]};
});
You could take a hash table for same labels and iterate the outer array and the inner array. If a label is not found, it generates a new entry for the result set.
var sizes = [{ id: [{ value: '2496', label: 'XS' }, { value: '2499', label: 'S' }], type: 'First Size' }, { id: [{ value: '2863', label: 34 }, { value: '2866', label: 36 }], type: 'Shoe Sizes' }, { id: [{ value: '3561', label: 'XS' }, { value: '3563', label: 'S' }, { value: '3565', label: 'L' }, { value: '3567', label: 'XL' }] }, { id: [{ value: '3523', label: 34 }, { value: '2866', label: 36 }], type: 'Shoe Sizes' }],
labels = Object.create(null),
joined = sizes.reduce((r, a) => {
var temp;
a.id.forEach(o => {
if (labels[o.label]) {
labels[o.label].value += ',' + o.value;
return;
}
if (!temp) {
temp = Object.assign({}, a, { id: [] });
r.push(temp);
}
temp.id.push(labels[o.label] = o);
});
return r;
}, []);
console.log(joined);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here you are, the code below would output Array called result, which is data set you desired, the loop is clear so I think it won't be an issue for you to go through it:
const sizes = [{
id: [{
value: '2496',
label: 'XS'
}, {
value: '2499',
label: 'S'
}],
type: 'First Size'
}, {
id: [{
value: '2863',
label: 34
}, {
value: '2866',
label: 36
}],
type: 'Shoe Sizes'
}, {
id: [{
value: '3561',
label: 'XS'
}, {
value: '3563',
label: 'S'
}, {
value: '3565',
label: 'L'
}, {
value: '3567',
label: 'XL'
}]
}, {
id: [{
value: '3523',
label: 34
}, {
value: '2866',
label: 36
}],
type: 'Shoe Sizes'
}]
var groupedSizes = {};
for (var current, i=0;i < sizes.length ;i++){
for (var j=0;j < sizes[i]['id'].length;j++) {
current = sizes[i]['id'][j]
if (groupedSizes[current['label']] !== undefined) {
groupedSizes[current['label']].push(current['value'])
} else {
groupedSizes[current['label']] = [current['value']]
}
}
}
var result = []
for (var key in groupedSizes) {
result.push({'id': groupedSizes[key].join(','), 'label': key})
}
console.log(result)