I have a list of team objects which consists team name, reported by and list of statuses for each day of that team which I would like to convert it into list of date based objects which contains all teams and its status for the respective date.
I have tried something like this which is not giving expected results.
teams.map(team => ({
day: team.statuses.reduce((acc, it) => it.day),
teams: {
teamName: team.teamName
}
}))
[
{
"teamName":"abc",
"reportedBy": "user1",
"statuses":[
{
"day":"10/12",
"status":"green"
},
{
"day":"10/11",
"status":"green"
},
{
"day":"10/09",
"status":"green"
}
]
},
{
"teamName":"xyz",
"reportedBy": "user2",
"statuses":[
{
"day":"10/12",
"status":"red"
},
{
"day":"10/11",
"status":"red"
},
{
"day":"10/09",
"status":"red"
}
]
}
]
Expected Output:
[
{
"day": "1012",
"teams": [
{
"teamName": "abc",
"reportedBy": "user1",
"status": "green"
},
{
"teamName": "xyz",
"reportedBy": "user2",
"status": "red"
}
]
},
{
"day": "1011",
"teams": [
{
"teamName": "abc",
"reportedBy": "user1",
"status": "green"
},
{
"teamName": "xyz",
"reportedBy": "user2",
"status": "red"
}
]
},
{
"day": "1009",
"teams": [
{
"teamName": "abc",
"reportedBy": "user1",
"status": "green"
},
{
"teamName": "xyz",
"reportedBy": "user2",
"status": "red"
}
]
}
]
I don't think reduce is the best option here. I think it's better to create an empty array and populate as you go:
let input = [
{
"teamName":"abc",
"reportedBy": "user1",
"statuses":[
{
"day":"10/12",
"status":"green"
},
{
"day":"10/11",
"status":"green"
},
{
"day":"10/09",
"status":"green"
}
]
},
{
"teamName":"xyz",
"reportedBy": "user2",
"statuses":[
{
"day":"10/12",
"status":"red"
},
{
"day":"10/11",
"status":"red"
},
{
"day":"10/09",
"status":"red"
}
]
}
];
let output = [];
input.forEach(team =>
{
let name = team.teamName;
let reporter = team.reporter
team.statuses.forEach(status =>
{
let dayItem = output.find(item => item.day == status.day);
if(dayItem == null)
{
let newItem = {day: status.day, teams:[{status: status.status, reportedBy: reporter, teamName: name}]};
output.push(newItem);
}
else
{
dayItem.teams.push({status: status.status, reportedBy: reporter, teamName: name})
}
});
});
console.log(JSON.stringify(output));
I have tried something like following.
let dayIndexed = {};
let dayStatuses = [];
teams.forEach(function(team) {
team.statuses.forEach(function(status) {
status.day = status.day.replace(/\//g, '');
if(!dayIndexed[status.day]) {
dayIndexed[status.day] = {
day: status.day,
teams: []
};
dayStatuses.push(dayIndexed[status.day]);
}
dayIndexed[status.day].teams.push({
teamName: team.teamName,
reportedBy: team.reportedBy,
status: status.status
});
});
});
Related
Javascript
I have a nested array of objects, I'm trying to filter the given array of objects using a property from the third level of its array property value. For example, from the below array I like to filter the entire array using the property ListId: 10
Example
let test = {
"test":true,
"group":[
{
"name":"header",
"value":[
{
"id":"0",
"list":[
{
"ListId":10,
"name":"string1",
"state":"BY",
"techId":0
},
{
"ListId":11,
"name":"string2",
"state":"BY"
},
{
"ListId":12,
"name":"string3",
"state":"BY"
}
]
}
]
},
{
"name":"header2",
"value":[
{
"id":"01",
"list":[
{
"ListId":100,
"name":"string1",
"state":"BY",
"techId":0
},
{
"ListId":111,
"name":"string2",
"state":"BY"
},
{
"ListId":121,
"name":"string3",
"state":"BY"
}
]
}
]
}
]
}
Filtervalue with ListId = 10
Expected output :
{
"test":true,
"group":[
{
"name":"header",
"value":[
{
"id":"0",
"list":[
{
"ListId":10,
"name":"string1",
"state":"BY",
"techId":0
}
]
}
]
}
]
}
How can I use the filter method using javascript to get this expected result?
You can two it in two times :
First, filter the list arrays,
Secondly filter the groups array using the some method
let test= {
"test": true,
"group": [
{
"name": "header",
"value": [
{
"id": "0",
"list": [
{
"ListId": 10,
"name": "string1",
"state": "BY",
"techId": 0
},
{
"ListId": 11,
"name": "string2",
"state": "BY"
},
{
"ListId": 12,
"name": "string3",
"state": "BY"
}
]
}
]
},
{
"name": "header2",
"value": [
{
"id": "01",
"list": [
{
"ListId": 100,
"name": "string1",
"state": "BY",
"techId": 0
},
{
"ListId": 111,
"name": "string2",
"state": "BY"
},
{
"ListId": 121,
"name": "string3",
"state": "BY"
}
]
}
]
}
]
}
test.group.forEach(group => {
group.value.forEach(value => {
value.list = value.list.filter(list => list.ListId === 10)
})
})
test.group = test.group.filter(group => group.value.some(value => value.list.length > 0))
console.log(test)
Note : You should use plural names for you arrays, it helps understanding the data. For example lists not list for the array.
let z ={"group1": [
{
"name": "header",
"value": [
{
"id": 0,
"list": [
{
"ListId": 10,
"Name": "string1"
},
{
"ListId": 11,
"Name": "string2"
}
]
}
]
}
]}
// This function was written from understading that 'group1' is not a fixed property, but part of a dynamic list due to the number '1'
const getItemByListId = (list, listId) => {
const listKeys = Object.keys(list);
const selectedListKey = listKeys.find(key => {
const groupItems = list[key];
const selectedItem = groupItems.find(({ value: nestedItems }) => {
const selectedNestedItem = nestedItems.find(({ list }) => {
const selectedList = list.find(({ ListId }) => ListId === listId)
return selectedList;
});
return selectedNestedItem;
});
return selectedItem;
});
if (!selectedListKey) {
return null;
}
return list[selectedListKey];
};
console.log(getItemByListId(z, 10));
Trying to filter an array of objects by the properties of another array of objects:
Data that I have
const filters = [
{
"id": "breed",
"value": "bulldog"
},
{
"id": "owner",
"value": "Kevin"
}
]
const dogs = [
{
"name": "Zephyr",
"breed": "bulldog",
"owner":"Kevin"
},
{
"name": "Dude",
"breed": "labradoodle",
"owner":"Lance"
},
{
"name": "Mark",
"breed": "yorkie",
"owner":"Rob"
},
{
"name": "Cheddar",
"breed": "bulldog",
"owner":"Kevin"
},
]
Output should be look like that:
[
{
name: "Cheddar",
breed: "bulldog",
owner: "Kevin",
},
{
name: "Zephyr",
breed: "bulldog",
owner: "Kevin",
},
];
But instead I am getting just:
[
{
"name": "Zephyr",
"breed": "bulldog",
"owner": "Kevin"
}
]
This is what I am doing:
dogs.reduce((acc, obj) => {
filters.forEach((element) => {
if (dogs.find((i) => i[element.id].includes(element.value))) {
if (!acc.some((e) => e[element.id].includes(element.value))) {
acc.push({ ...obj });
}
}
});
return acc;
}, []);
Using Array#filter and Array#every, get the list of dogs meeting the filters
const
filters = [ { "id": "breed", "value": "bulldog" }, { "id": "owner", "value": "Kevin" } ],
dogs = [ { "name": "Zephyr", "breed": "bulldog", "owner":"Kevin" }, { "name": "Dude", "breed": "labradoodle", "owner":"Lance" }, { "name": "Mark", "breed": "yorkie", "owner":"Rob" }, { "name": "Cheddar", "breed": "bulldog", "owner":"Kevin" } ];
const filteredDogs = dogs.filter(dog =>
filters.every(({ id, value }) => dog[id] === value)
);
console.log(filteredDogs);
Here is your ans:
const result = dogs.filter(dog => {
return filters.every(filter => dog[filter.id] == filter.value)
})
Code snippet
const filters = [
{
"id": "breed",
"value": "bulldog"
},
{
"id": "owner",
"value": "Kevin"
}
]
const dogs = [
{
"name": "Zephyr",
"breed": "bulldog",
"owner":"Kevin"
},
{
"name": "Dude",
"breed": "labradoodle",
"owner":"Lance"
},
{
"name": "Mark",
"breed": "yorkie",
"owner":"Rob"
},
{
"name": "Cheddar",
"breed": "bulldog",
"owner":"Kevin"
},
];
const result = dogs.filter((dog)=> {
return filters.every((filter)=> dog[filter.id] == filter.value)
})
console.log(result)
I'm using mongoose.
Just like 'Mysql Join',
I want to get the data that merge the parents and children collection below.
Parent
[
{
type: "A",
results: [
{
"id": 111111
},
{
"id": 222222
}
]
},
{
type: "B",
results: [
{
"id": 333333
},
{
"id": 444444
}
]
}
]
Child
[
{
dataId: 111111,
results: [
{ "status": { key: "value" } }
]
},
{
dataId: 222222,
results: [
{ "status": { key: "value" } }
]
},
{
dataId: 333333,
results: [
{ "status": { key: "value" } }
]
},
{
dataId: 444444,
results: [
{ "status": { key: "value" } }
]
},
]
Because ObjectId cannot be inserted,
it seems that it cannot be processed using the population method.
I want to merge the two data like Join in MySQL.
like below
Parent.find()
[
{
type: "A",
results: [
{
"id": 111111,
results: [
{ "status": { key: "value" } }
]
},
{
"id": 222222,
results: [
{ "status": { key: "value" } }
]
}
]
},
{
type: "B",
results: [
{
"id": 333333,
results: [
{ "status": { key: "value" } }
]
},
{
"id": 444444,
results: [
{ "status": { key: "value" } }
]
}
]
}
]
You can use this query:
$unwind to get every result from the array to merge with the Child dataid.
$lookup which is the "join" in mongodb. Here query is mergin field id into results from the Parent with dataId from the Child.
$unwind again because $lookup creates an array.
$group to group the values according the id.
$project (this stage is optional) to not shown fields you don't want.
yourParentModel.aggregate([
{
"$unwind": "$results"
},
{
"$lookup": {
"from": "Child",
"localField": "results.id",
"foreignField": "dataId",
"as": "child_results"
}
},
{
"$unwind": "$child_results"
},
{
"$group": {
"_id": "$_id",
"type": {
"$first": "$type"
},
results: {
"$push": "$child_results"
}
}
},
{
"$project": {
"_id": 0,
"results._id": 0
}
}
])
Example here
you could do this from javascript directly
first you get the parent from mongoDB
( use mongoose find method)
let parents = [
{
type: "A",
results: [
{
id: 111111,
},
{
id: 222222,
},
],
},
{
type: "B",
results: [
{
id: 333333,
},
{
id: 444444,
},
],
},
];
then you get the child from the database
let children = [
{
dataId: 111111,
results: [{ status: { key: "value" } }],
},
{
dataId: 222222,
results: [{ status: { key: "value" } }],
},
{
dataId: 333333,
results: [{ status: { key: "value" } }],
},
{
dataId: 444444,
results: [{ status: { key: "value" } }],
},
];
and the treatement to merge the parents with the children will be something like this
for (let parent of parents) {
for (let objectId of parent.results) {
for (let child of children) {
if (child.dataId === objectId.id) {
objectId.results = child.results;
break;
}
}
}
}
I am having a dynamic JSON array in below format,
let main_data = [
{
"client":[
{
"name":"aaaa",
"count":"1",
"filter":{
"type":{
"name":"test3"
}
}
},
{
"name":"bbbb",
"count":"9",
"filter":{
"type":{
"name":"test2"
}
}
}
]
},
{
"compute":[
{
"name":"cccc",
"count":"6",
"filter":{
"type":{
"name":"test"
}
}
}
]
}
]
Here key "name" is unique. When updating a form, I will get an json array like below,
let new_data = [
{
"client":[
{
"name":"bbbb",
"count":"1234",
"type":{
"name":"updated_name"
}
}
}
]
}
]
I need to check the "name" in the json array in "main_data" and remove the existing one and update with the new "updated_data" into the "main_data". (no Jquery please)
Expected output,
let main_data = [
{
"client":[
{
"name":"aaaa",
"count":"1",
"filter":{
"type":{
"name":"test3"
}
}
},
{
"name":"bbbb",
"count":"123",
"filter":{
"type":{
"name":"updated_name"
}
}
}
]
},
{
"compute":[
{
"name":"cccc",
"count":"6",
"filter":{
"type":{
"name":"test"
}
}
}
]
}
]
Is there any way to achive this. Any help would be much appreciated. Thanks in advance.
Try this
let main_data = [{
client: [{
name: "aaaa",
count: "1",
filter: {
type: {
name: "test3"
}
}
},
{
name: "bbbb",
count: "9",
filter: {
type: {
name: "test2"
}
}
}
]
},
{
compute: [{
name: "cccc",
count: "6",
filter: {
type: {
name: "test"
}
}
}]
}
];
let new_data = [{
client: [{
name: "bbbb",
count: "1234",
filter: {
type: {
name: "updated_name"
}
}
}]
}];
const res = main_data.map((item, index) => {
if (item.client) {
const clients = item.client.map(client => {
if (client.name === new_data[0].client[0].name) {
client = new_data[0].client[0];
}
return client;
});
return {
client: clients
};
}
return item;
});
console.log(res);
There may very well be a fancy way to get this done but one can always just find the matching item and replace it. eg.
let main_data = [
{
"client": [
{
"name": "aaaa",
"count": "1",
"filter": {
"type": {
"name": "test3"
}
}
},
{
"name": "bbbb",
"count": "123",
"filter": {
"type": {
"name": "updated_name"
}
}
}
]
},
{
"compute": [
{
"name": "cccc",
"count": "6",
"filter": {
"type": {
"name": "test"
}
}
}
]
}
];
let new_data = [
{
"client": [
{
"name": "bbbb",
"count": "1234",
"type": {
"name": "updated_name"
}
}
]
}
];
console.log("before:" + JSON.stringify(main_data));
newItem = new_data[0]["client"][0];
mainDataList = main_data[0]["client"];
for (i = 0; i < mainDataList.length; i++) {
if (mainDataList[i].name == newItem.name) {
mainDataList[i] = newItem;
}
}
console.log("after:" + JSON.stringify(main_data));
will output
before:[{"client":[{"name":"aaaa","count":"1","filter":{"type":{"name":"test3"}}},{"name":"bbbb","count":"123","filter":{"type":{"name":"updated_name"}}}]},{"compute":[{"name":"cccc","count":"6","filter":{"type":{"name":"test"}}}]}]
after:[{"client":[{"name":"aaaa","count":"1","filter":{"type":{"name":"test3"}}},{"name":"bbbb","count":"1234","type":{"name":"updated_name"}}]},{"compute":[{"name":"cccc","count":"6","filter":{"type":{"name":"test"}}}]}]
Here's a simple way to do this, let's say your new data is at variable newData:
main_data.client.filter(item => item.name === newData.name).push(newData)
I am trying to figure out the most performant Javascript way to convert an array of objects, into an object with unique keys and an array full of objects as the value.
For Example:
const array = [
{ "name": "greg", "year": "2000" },
{ "name": "john", "year": "2002" },
{ "name": "bob", "year": "2005" },
{ "name": "ned", "year": "2000" },
{ "name": "pam", "year": "2000" },
];
I would like this converted to:
{
"2000": [
{ "name": "greg", "year": "2000" },
{ "name": "ned", "year": "2000" },
{ "name": "pam", "year": "2000" }
],
"2002": [ { "name": "john", "year": "2002" } ],
"2005": [ { "name": "bob", "year": "2005" } ],
}
As of now, this is what I've done so far:
let yearsObj = {};
for (let i=0; i<array.length; i++) {
if (!yearsObj[array[i].year]) {
yearsObj[array[i].year] = [];
}
yearsObj[array[i].year].push(array[i]);
}
you can use a more elegant way to do it by using array's reduce function
// # impl
const group = key => array =>
array.reduce(
(objectsByKeyValue, obj) => ({
...objectsByKeyValue,
[obj[key]]: (objectsByKeyValue[obj[key]] || []).concat(obj)
}),
{}
);
// # usage
console.log(
JSON.stringify({
byYear: group(array),
}, null, 1)
);
// output
VM278:1 {
"carsByBrand": {
"2000": [
{
"name": "greg",
"year": "2000"
},
{
"name": "ned",
"year": "2000"
},
{
"name": "pam",
"year": "2000"
}
],
"2002": [
{
"name": "john",
"year": "2002"
}
],
"2005": [
{
"name": "bob",
"year": "2005"
}
]
}
}
It could be as simple as that Object.fromEntries(array.map(obj => [obj.year,obj])) even it is not exactly what you need, but talking about performance it is way slower than all proposed, so i'm giving it as an bad example of showing how the short statement is not always the fastest.
Your way seems to be the fastest taking about performance.
Run the snippet below to see the actual timing.
// common
let array = [
{ "name": "greg", "year": "2000" },
{ "name": "john", "year": "2002" },
{ "name": "bob", "year": "2005" },
{ "name": "ned", "year": "2000" },
{ "name": "pam", "year": "2000" },
];
// simple as a statement way
console.time();
console.log(Object.fromEntries(array.map(obj => [obj.year,obj])));
console.timeEnd();
// using .reduce way
console.time();
const result = array.reduce((prev, curr) => {
const { year } = curr;
if (prev[year]) {
prev[year].push(curr);
} else {
prev[year] = [curr];
}
return prev;
}, {});
console.log(result);
console.timeEnd();
// your way
console.time();
let yearsObj = {};
for (let i=0; i<array.length; i++) {
if (!yearsObj[array[i].year]) {
yearsObj[array[i].year] = [];
}
yearsObj[array[i].year].push(array[i]);
}
console.log(yearsObj);
console.timeEnd();
A for loop (imperative style) like you have is likely to be the fastest in most situations. However, in this case you are not likely to see much of a difference. One thing you could do to improve the code in your example is to get the array length before the for loop and assign it to the variable, so that it's not calculated every iteration of the loop.
const yearsObj = {};
const arrayLength = array.length; // Only calculate array length once
for (let i=0; i<arrayLength; i++) {
if (!yearsObj[array[i].year]) {
yearsObj[array[i].year] = [];
}
yearsObj[array[i].year].push(array[i]);
}
In this situation, my preference would be to use Array.reduce(). It is more readable and the performance difference will be negligible.
const arr = [
{ name: 'greg', year: '2000' },
{ name: 'john', year: '2002' },
{ name: 'bob', year: '2005' },
{ name: 'ned', year: '2000' },
{ name: 'pam', year: '2000' },
];
const result = arr.reduce((prev, curr) => {
const { year } = curr;
if (prev[year]) {
prev[year].push(curr);
} else {
prev[year] = [curr];
}
return prev;
}, {});
/* Result:
{ '2000':
[ { name: 'greg', year: '2000' },
{ name: 'ned', year: '2000' },
{ name: 'pam', year: '2000' } ],
'2002': [ { name: 'john', year: '2002' } ],
'2005': [ { name: 'bob', year: '2005' } ] }
*/