Structuring an array the correct way with lodash groupBy - javascript

I have an object that looks like this:
{
data: [
{ id: "1", state: "accepted", estimate_date: "2019-12-17" },
{ id: "2", state: "rejected", estimate_date: "2019-12-17" },
{ id: "3", state: "open", estimate_date: "2019-12-17" },
{ id: "4", state: "open", estimate_date: "2019-12-18" },
{ id: "5", state: "rejected", estimate_date: "2019-12-18" },
{ id: "6", state: "accepted", estimate_date: "2019-12-18" },
]
}
When I use lodash groupBy on the object like this:
const key = 'data';
const groupedEstimates = groupBy(estimateData[key], 'estimate_date');
It returns:
[
[
"2019-12-17"
],
[
[ { id: "1", state: "accepted" } ],
[ { id: "2", state: "rejected" } ],
[ { id: "3", state: "open" } ]
]
],
[
[
"2019-12-18"
],
[
[ { id: "4", state: "open" } ],
[ { id: "5", state: "rejected" } ],
[ { id: "6", state: "accepted" } ]
]
]
But now I'm trying to achieve something like this:
[
{
date: "2019-12-17",
items: [
{ id: "1", state: "accepted" },
{ id: "2", state: "rejected" },
{ id: "3", state: "open" },
]
},
{
date: "2019-12-18",
items: [
{ id: "4", state: "open" },
{ id: "5", state: "rejected" },
{ id: "6", state: "accepted" },
]
}
]
Except I don't know how to achieve that using lodash. It doesn't have to use lodash but I only used that at the start as it seemed an easy solution to my problem. Now that I'm trying to achieve a more sensible data structure I would like some insights on how to achieve it.

After grouping by the estimate_date property, iterate the groups object with _.map(). Generate the group's object by taking the key (2nd param) for the date property, and mapping the items to omit the estimate_date:
const estimateData = {"data":[{"id":"1","state":"accepted","estimate_date":"2019-12-17"},{"id":"2","state":"rejected","estimate_date":"2019-12-17"},{"id":"3","state":"open","estimate_date":"2019-12-17"},{"id":"4","state":"open","estimate_date":"2019-12-18"},{"id":"5","state":"rejected","estimate_date":"2019-12-18"},{"id":"6","state":"accepted","estimate_date":"2019-12-18"}]}
const groupedEstimates = _.map(
_.groupBy(estimateData.data, 'estimate_date'),
(items, date) => ({
date,
items: items.map(o => _.omit(o, 'estimate_date'))
})
)
console.log(groupedEstimates)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Related

How to loop through an array of objects and add a new field using react and javascript?

i want to loop through array of objects and check for a particular property and add a new property "disabled" to the array.
below is the array of objects
const arr_obj = [
{
id: "1",
name: "name1",
type: "type2",
children: [
{
id: "2",
name: "name2",
type: "type4",
children: [
{
id: "3",
name: "name3",
type: "type5",
},
},
{
id: "4",
name: "name4",
type: "type3",
children: [
{
id: "5",
name: "name5",
type: "type4",
children: [],
},
],
},
id: "6",
name: "name6",
type: "type3",
children: [
{
id: "7",
name: "name7",
type: "type4",
children: [],
},
],
}
},
.....//similar objects
];
so from above array of objects i want to check for each object if type === type2 and if type2 then add property disabled: false if not disabled: true.
below is what i have tried
const new_obj = React.useMemo(() => {
return arr_obj.map((arr) => ({
...arr,
disabled: arr?.type !== "type2" ? true : false,
}));
}, [arr_obj]);
this adds disabled property only to outer object it doesnt add to children object.
output with above snippet is like below,
const new_arr = [
{
id: "1",
name: "name1",
type: "type2",
disabled: false,
children: [
{
id: "2",
name: "name2",
type: "type4",
children: [
{
id: "3",
name: "name3",
type: "type5",
},
},
{
id: "4",
name: "name4",
type: "type3",
children: [
{
id: "5",
name: "name5",
type: "type4",
children: [],
},
],
},
id: "6",
name: "name6",
type: "type3",
children: [
{
id: "7",
name: "name7",
type: "type4",
children: [],
},
],
}
},
.....//similar objects
];
expected output is like below,
const new_arr = [
{
id: "1",
name: "name1",
type: "type2",
disabled: false,
children: [
{
id: "2",
name: "name2",
type: "type4",
disabled: true,
children: [
{
id: "3",
name: "name3",
type: "type5",
disabled: true,
},
},
{
id: "4",
name: "name4",
type: "type3",
disabled: true,
children: [
{
id: "5",
name: "name5",
type: "type4",
disabled: true,
children: [],
},
],
},
id: "6",
name: "name6",
type: "type3",
disabled: true
children: [
{
id: "7",
name: "name7",
type: "type4",
disabled: true,
children: [],
},
],
}
},
.....//similar objects
];
How can i fix the above snippet such that it adds disabled property to children too. could someone help me with this. thanks.
EDIT:
tried answer is like below,
function loop_children(children) {
if (!children || children.lengh <=0) {
return;
} else {
return children.map((child) => {
...child,
disabled: child?.type !== "type2" ? true : false,
children: loop_children(children)
})
};
}
}
return arr_obj.map((arr) => ({
...arr,
disabled: arr?.type !== "type2" ? true : false,
children: loop_children(arr.children) //seems like a problem here in adding children field again
}));
but this adds children array under children again.
This code doesnt work. it adds field disabled to children but also adds children within children.
could someone help me with this. thanks.
Not sure why all the others are mapping, just alter the object with a simple recursive call when it has a children property.
const arr_obj = [{
id: "1",
name: "name1",
type: "type2",
children: [{
id: "2",
name: "name2",
type: "type4",
children: [{
id: "3",
name: "name3",
type: "type5",
}, ]
}, ]
}];
const disableEnableObj = (arr, type) => {
arr.forEach(obj => {
obj.disabled = obj.type !== type;
obj.children && disableEnableObj(obj.children, type);
});
}
disableEnableObj(arr_obj, 'type2');
console.log(arr_obj);
You have to loop through the children too. It should look something like this:
function loop_children(children) {
return children.map((child) => {
...child,
disabled: child?.type !== "type2" ? true : false,
children: loop_children(children)
})
}
return arr_obj.map((arr) => ({
...arr,
disabled: arr?.type !== "type2" ? true : false,
children: loop_children(children)
}));

JS filtering an Array item through a value in a nested Array

I can't figure out how to properly use .filter() to find an object in an Array by searching for a value in its nested Array.
I have the following data:
const orders = [
{
id: 1,
items: [
{
itemId: "abc",
},
{
itemId: "def",
},
],
},
{
id: 2,
items: [
{
itemId: "jkl",
},
{
itemId: "mno",
},
],
},
{
id: 3,
items: [
{
itemId: "abc",
},
{
itemId: "xyz",
},
],
},
];
I have the needed itemId: "abc" which I need to use to find all the objects that have items that also have the Id of "abc", so that the result is this:
[
{
id: 1,
items: [
{
itemId: "abc",
},
{
itemId: "def",
},
],
},
{
id: 3,
items: [
{
itemId: "abc",
},
{
itemId: "xyz",
},
],
},
]
So far, I've tried the following:
orders &&
orders.filter((order) => {
return order.items.filter((item) => item.itemId === "abc");
});
But it doesn't seem to work. What am I missing here?
Chris G beat me to it in the comments but he is right, you need to use order.items.some in your inner function:
const orders = [{
id: 1,
items: [{
itemId: "abc",
},
{
itemId: "def",
},
],
},
{
id: 2,
items: [{
itemId: "jkl",
},
{
itemId: "mno",
},
],
},
{
id: 3,
items: [{
itemId: "abc",
},
{
itemId: "xyz",
},
],
},
]
var ans = orders.filter((order) => {
return order.items.some((item) => item.itemId === "abc");
});
console.log(ans)
const orders = [{
id: 1,
items: [{
itemId: "abc",
},
{
itemId: "def",
},
],
},
{
id: 2,
items: [{
itemId: "jkl",
},
{
itemId: "mno",
},
],
},
{
id: 3,
items: [{
itemId: "abc",
},
{
itemId: "xyz",
},
],
},
];
const result = orders.filter(order =>
order.items.find(item => item.itemId === 'abc') !== undefined
)
console.log(result);

How to segregate an array of object by using map?

I have to form an array of object in another array of object based on id. I was able to group the object based the "applicationId" but was not able to group the inside array of attributes and values array. Also the above code gives me duplicate objects. Please help with this I know its a small fix but I spend whole day still no result.stackblitz. Expected output commented below in stackblitz
data.map((el) => {
el.attribute.map((elm) => {
elm.options.map(em => {
permissions.push({
applicationId: el.application, attributes: [{ name: elm.name, value: em.value, disabled: true
}]
})
})
})
});
Input Object
[
{
application: "abc",
attribute: [
{
description: "abc description1"
name: "audio"
options:[
{name: "Yes", value: "Y"}
{name: "No", value: "N"}
]
},
{
description: "abc description2"
name: "video"
options:[
{name: "true", value: "T"}
{name: "false", value: "F"}
]
}
{
description: "abc description3"
name: "call"
options:[
{name: "Yes", value: "Y"}
{name: "false", value: "F"}
]
}
]
},
{
application: "def",
attribute: [
{
description: "def description1"
name: "audio"
options:[
{name: "Yes", value: "Y"}
{name: "No", value: "N"}
]
},
{
description: "def description2"
name: "video"
options:[
{name: "true", value: "T"}
{name: "false", value: "F"}
]
}
{
description: "def description3"
name: "call"
options:[
{name: "Yes", value: "Y"}
{name: "false", value: "F"}
]
}
]
}
]
Expected Output:
permissions:[
{
applicationId:abc
attributes:
[
{
name:audio
value:["Y","N"]
disabled: true
},
{
name:video,
value:["T","F"]
disabled: true
},
{
name:call,
value:["Y","F"]
disabled: true
}
]
},
{
applicationId: def
attributes:
[
{
name:audio
value:["Y","N"]
disabled: true
},
{
name:video,
value:["T","F"]
disabled: true
},
{
name:call,
value:["Y","F"]
disabled: true
}
]
}
]
You could do so using few array maps.
Try the following
var input = [ { application: "abc", attribute: [ { description: "abc description1", name: "audio", options: [ { name: "Yes", value: "Y" }, { name: "No", value: "N" } ] }, { description: "abc description2", name: "video", options: [ { name: "true", value: "T" }, { name: "false", value: "F" } ] }, { description: "abc description3", name: "call", options: [ { name: "Yes", value: "Y" }, { name: "false", value: "F" } ] } ] }, { application: "def", attribute: [ { description: "def description1", name: "audio", options: [ { name: "Yes", value: "Y" }, { name: "No", value: "N" } ] }, { description: "def description2", name: "video", options: [ { name: "true", value: "T" }, { name: "false", value: "F" } ] }, { description: "def description3", name: "call", options: [ { name: "Yes", value: "Y" }, { name: "false", value: "F" } ] } ] } ];
var output = input.map(item => ({
applicationId: item.application,
attributes: item.attribute.map(attr => ({
name: attr.name,
value: attr.options.map(option => option.value),
disabled: true
}))
}));
console.log(output);
I've modified your Stackblitz.
Sidenote: Please try to post a valid object in the question. It'll make it much easier and quicker to reproduce the issue. Much of my time was spent not in the solution but in fixing the object.

display countries based on selection of id from a complex JSON structure using Javascript Map or Filter Method

I have a JSON structure which is bit complex and I want to display the corresponding items based on the selection of Id. For example, in my JSON structure I have the sections of continent and under each continent countries are listed within the property names "Levels".So, I want to filter each continent based on the id from the "Levels" property.
So, if the "Country.Level.Id === 6" it should display the Country.Name="America". I have been trying to make it work by using JavaScript Map and Filter method but couldn't make it work.
Here is my SandBox Link: https://codesandbox.io/s/dark-feather-ugvxv?file=/src/index.js:0-1294
The data:
const items = [
{
Country: [
{
Id: "1",
Name: "Europe",
Levels: [
{
Id: "1",
Name: "Finland",
DisplayName: "Finland"
}
]
},
{
Id: "1",
Name: "ASIA",
Levels: [
{
Id: "2",
Name: "Bangladesh",
DisplayName: "Bangladesh"
},
{
Id: "3",
Name: "India",
DisplayName: "India"
},
{
Id: "4",
Name: "Pakistan",
DisplayName: "Pakistan"
}
]
},
{
Id: "3",
Name: "America",
Levels: [
{
Id: "5",
Name: "USA",
DisplayName: "USA"
},
{
Id: "6",
Name: "Canada",
DisplayName: "Canada"
},
{
Id: "7",
Name: "Australia",
DisplayName: "Australia"
}
]
},
{
Id: "4",
Name: "Africa",
DisplayName: "Africa",
Levels: [
{
Id: "8",
Name: "Nigeria",
DisplayName: "Nigeria"
}
]
}
]
}
];
My coding attempt:
const selectById = "5";
const countries = items[0].Country;
const result = countries.filter(item => item.Levels.Id === selectById);
console.log(result);
According to your data structure, this function will find a continent from a country id:
function findContinentByCountryId(items, id) {
return items[0].Country.find((continent) => {
return continent.Levels.some(country => country.Id === id)
})
}
You can test it with the snippet below:
function findContinentByCountryId(items, id) {
return items[0].Country.find((continent) => {
return continent.Levels.some(country => country.Id === id)
})
}
<form>
<label for="country-id">Country id:</label>
<input id="country-id">
<input type="submit" value="Find continent by country id">
</form>
<span></span>
<script>
const items = [
{
Country: [
{
Id: "1",
Name: "Europe",
Levels: [
{
Id: "1",
Name: "Finland",
DisplayName: "Finland"
}
]
},
{
Id: "1",
Name: "ASIA",
Levels: [
{
Id: "2",
Name: "Bangladesh",
DisplayName: "Bangladesh"
},
{
Id: "3",
Name: "India",
DisplayName: "India"
},
{
Id: "4",
Name: "Pakistan",
DisplayName: "Pakistan"
}
]
},
{
Id: "3",
Name: "America",
Levels: [
{
Id: "5",
Name: "USA",
DisplayName: "USA"
},
{
Id: "6",
Name: "Canada",
DisplayName: "Canada"
},
{
Id: "7",
Name: "Australia",
DisplayName: "Australia"
}
]
},
{
Id: "4",
Name: "Africa",
DisplayName: "Africa",
Levels: [
{
Id: "8",
Name: "Nigeria",
DisplayName: "Nigeria"
}
]
}
]
}
];
const form = document.querySelector('form')
const input = document.querySelector('input')
const result = document.querySelector('span')
form.addEventListener('submit', (event) => {
event.preventDefault()
const continent = findContinentByCountryId(items, input.value)
result.textContent = continent ? continent.Name : 'Continent not found'
})
</script>
Side note
I don't know the context of your problem, but be aware that object properties are usually camelCased in javascript (instead of PascalCased in your case) and that Country and Levels might not be ideal names for the corresponding data. Furthermore, in case this wasn't intentional, Europe and Asia have the same id in your dataset.
You have to change your filter method appropriately it'll work
countries.filter(item => item.Levels.find(elem => elem.Id === selectById))
SandboxLink

Aggregate data from nested array

I need help with the aggregate framework.
I have a model (currencies field can contain more than one object):
const schema = new mongoose.Schema({
country: { type: String },
code: { type: String },
region: [{
name: { type: String },
path: { type: Array },
city: [{
name: { type: String },
path: { type: Array },
latitude: { type: String },
longitude: { type: String },
}],
}],
currencies: [{
code: { type: String },
name: { type: String },
symbol: { type: String },
}],
})
And I need to receive all currencies without duplicates.
Received data can view like this:
[
{ code: 'string', name: 'sting', symbol: 'string' },
{ code: 'string', name: 'sting', symbol: 'string' },
...
]
// or like this:
[
currencies: [
{ code: 'string', name: 'sting', symbol: 'string' },
{ code: 'string', name: 'sting', symbol: 'string' },
...
]
]
I try to create a query
Geo.aggregate([
{
$group: {
_id: null,
currencies: { $addToSet: '$currencies' },
},
},
])
but receive this data with duplicates and it has many nested arrays:
[
{
"_id": null,
"currencies": [
[
{
"_id": "5cd9486248989616a411fac5",
"code": "JPY",
"name": "Japanese yen",
"symbol": "¥"
}
],
[
{
"_id": "5cd9491a48989616a411fb47",
"code": "TRY",
"name": "Turkish lira",
"symbol": null
}
],
I try this query:
Geo.aggregate([
{
$addFields: {
code: '$currencies.code',
name: '$currencies.name',
symbol: '$currencies.symbol',
},
},
])
But I receive error "TypeError: item is not iterable".
I need little help )
Db data views like this:
{
"_id": {
"$oid": "5c3334a8871695568817eadf"
},
"country": "Singapore",
"code": "sg",
"region": [
{
"path": [
"Singapore"
],
"_id": {
"$oid": "5c3366c63d92ac6e531e05c0"
},
"city": [],
"name": "Central Singapore Community Development Council"
},
....
],
"__v": 0,
"currencies": [
{
"_id": {
"$oid": "5cd948ec48989616a411fb28"
},
"code": "BND",
"name": "Brunei dollar",
"symbol": "$"
},
{
"_id": {
"$oid": "5cd948ec48989616a411fb27"
},
"code": "SGD",
"name": "Singapore dollar",
"symbol": "$"
}
]
}
In aggregate pipeline first you need to unwind the currencies array and then group them by condition to get desired result.
Geo.aggregate([
{
$unwind: '$currencies'
},
{
$group: {
_id: null,
currencies: { $addToSet: '$currencies' },
},
},
])
For more information you can look into documentation here
db.temp.aggregate([
{$project : {currencies : 1}},
{$unwind: "$currencies"},
{
$addFields: {
currencyHash: {
$concat : ['$currencies.code', "--", "$currencies.name", "--", "$currencies.symbol"]
}
}
},
{
$group: {
_id: "$currencyHash",
currency : {
$first : "$currencies"
}
}
},
{
$project: {
code : "$currency.code",
name : "$currency.name",
symbol : "$currency.symbol"
}
},
{
$project: {
_id : 0,
currency : 0
}
}
]).pretty()

Categories

Resources