Related
This is the result I want to achieve
dataset: [
dataset: [
{
seriesname: "",
data: [
{
value: "123",
},
{
value: "123",
},
]
},
]
]
My problem right now is that the second dataset gets duplicated.
This is how I am setting it (val is an integer and allYears is an array of integers):
this.grphColumn.dataSource.dataset[0].dataset = this.allYears.map(el => {
return {
seriesname: "Planned",
data: [{value: val}, {value: val}]
}
});
How can I make it so the dataset doesn't get duplicated?
You have to map the values separately, if you dont want the seriesName to be Repeated..
const yearsMap = this.allYears.map((el) => { return { value: el } });
this.grphColumn.dataSource.dataset[0].dataset = {
seriesname: "Planned",
data: yearsMap
}
I have a filter object that is returned by query params
url = /all?channels=calls,text&calls=voicemail,missed
const query = {
channels: 'calls,texts',
calls: 'voicemail,missed',
};
I then have an array of objects that come in from a socket.
const arr = [
{
id: 1,
channel: 'SMS',
sent: '2021-08-22T03:21:18.41650+0000',
sender: {
contactType: 'business',
},
recipients: [
{
contactType: 'corporate',
},
],
direction: 'INBOUND',
},
{
id: 2,
channel: 'VOICE',
sent: '2021-08-20T23:15:56.00000+0000',
sender: {
contactType: 'business',
},
recipients: [
{
contactType: 'corporate',
},
],
callDetails: {
answered: false,
voicemail: true,
},
direction: 'INBOUND',
},
{
id: 3,
channel: 'VOICE',
sent: '2021-08-20T23:15:56.00000+0000',
sender: {
contactType: 'business',
},
recipients: [
{
contactType: 'corporate',
},
],
callDetails: {
answered: true,
voicemail: false,
},
direction: 'INBOUND',
},
{
id: 4,
channel: 'VOICE',
sent: '2021-08-20T23:15:56.00000+0000',
sender: {
contactType: 'business',
},
recipients: [
{
contactType: 'corporate',
},
],
callDetails: {
answered: false,
voicemail: false,
},
direction: 'INBOUND',
},
];
I want to filter out the objects that match the filters but the query obj isn't friendly enough to just map the arr through.
With the query obj shared above, i should return the objects id:1 and id:2 and id:4 from arr, since those object meet the criteria of sms, voicemail, & missed
I assume i need a modified query obj that has to have various conditions available for each property, i.e calls: voicemail === callDetails.voicemail === true or calls: received === callDetails.answered === true
I've seen lots of examples on how to filter an array of objects with multiple match-criteria, but with the req of the property having multiple conditions, i've hit a wall.
thanks for the help
The main idea is to provide kind of a rosetta stone which does bridge/map the query specific syntax with any list item's specific data structure. Thus one will end up writing a map which takes a query's structure into account but ensures for each necessary query endpoint an item specific filter condition/function.
The query function should simply filter the item list by applying a list of logical OR conditions, thus using some for returning the boolean filter value.
Which leaves one of implementing a helper method which collects ... via Object.entries and Array.prototype.flatMap as well as via String.prototype.split and Array.prototype.map ... the function endpoints from the above introduced requirements configuration/map, based on the query object, provided by the system. Thus this helper might be named resolveQuery.
const sampleList = [{
id: 1,
channel: 'SMS',
direction: 'INBOUND',
}, {
id: 2,
channel: 'VOICE',
callDetails: {
answered: false,
voicemail: true,
},
direction: 'INBOUND',
}, {
id: 3,
channel: 'VOICE',
callDetails: {
answered: true,
voicemail: false,
},
direction: 'INBOUND',
}, {
id: 4,
channel: 'VOICE',
callDetails: {
answered: false,
voicemail: false,
},
direction: 'INBOUND',
}];
// prepare a `requirements` map which ...
// - on one hand maps `query`-syntax to a list items's structure
// - and on the other hand does so by providing an item specific
// filter condition/function for each necessary query endpoint.
const requirements = {
channels: {
texts: item => item.channel === 'SMS',
},
calls: {
voicemail: item => item.channel === 'VOICE' && !!item.callDetails.voicemail,
missed: item => item.channel === 'VOICE' && !item.callDetails.answered,
},
}
// const query = {
// channels: 'calls,texts',
// calls: 'voicemail,missed',
// };
function resolveQuery(requirements, query) {
const reject = item => false;
// create/collect a list of filter condition/functions
// which later will be applied as logical OR via `some`.
return Object
.entries(query)
.flatMap(([ groupKey, groupValue ]) =>
// e.g groupKey => 'channels',
// groupValue => 'calls,texts'
groupValue
.split(',')
.map(requirementKey =>
// e.g requirementKey => 'calls'
// or requirementKey => 'texts'
requirements?.[groupKey]?.[requirementKey?.trim()] ?? reject
)
);
}
function queryFromItemList(itemList, requirements, query) {
const conditionList = resolveQuery(requirements, query);
console.log(
'conditionList ... [\n ',
conditionList.join(',\n '),
'\n]'
);
return itemList.filter(item =>
conditionList.some(condition => condition(item))
);
}
console.log(
queryFromItemList(sampleList, requirements, {
channels: 'calls,texts',
calls: 'voicemail,missed',
})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
i want to find if there is atleast one item with completed property with value false from an array of objects using javascript and react.
i have an array of objects like so,
const items = [
{
id: "32",
jobs: [
{
jobs_id: "32",
completed: true,
},
{
jobs_id: "34",
completed: false,
},
],
},
{
id: "31",
jobs: [
{
jobs_id: "30",
completed: true,
},
{
jobs_id: "33",
completed: true,
},
],
},
{
id: "30",
jobs: [
{
jobs_id: "31",
completed: true,
}
],
}
]
selected_items = {
32: 1,
31: 1,
}
Now i want to check if the items has atleast one object with job completed false for the id 32 and 31 .
basically i have to filter objects with selected_items and then check if the job object completed property value is false.
In the above example selected_items has 32 and 31 ids. so i have to filter items from items array of object with ids 32 and 31 and see if either of their jobs object has completed: false.
in above example we see that item with id "32" has job with job_id "34" has completed: false.
So i am expecting the output to be true if atleast one of the items with ids 32 or 31 has jobs with completed: false. otherwise false.
how can i check this using javascript.
I am new to programming and learning javascript. could someone help me with this thanks.
EDIT:
what i have tried?
i know that filtering is possible using filter and array.includes() method.
so if i had selected_items = [32,31] then i could do something like below to get items with id 32 and 31
const res = items.filter((item) => selected_items.includes(item.id);
but in my case selected_items is object. and this just filters the items for given ids. i want to still check if the items filtered based on ids have atleast on completed: false.
const items = [
{
id: "32",
jobs: [
{
id: "32",
completed: true,
},
{
id: "34",
completed: false,
},
],
},
{
id: "31",
jobs: [
{
id: "30",
completed: true,
},
{
id: "33",
completed: true,
},
],
},
{
id: "30",
jobs: [
{
id: "31",
completed: true,
}
],
}
]
const selected_items = { 32: 1, 31: 1, }
const filteredItems = items.filter(item => Object.keys(selected_items).includes(item.id))
console.log(filteredItems);
const extractCompletedValues = filteredItems.map(item => item.jobs.map(job => job.completed))
console.log(extractCompletedValues)
// returns the array from [[true, false]] to [ true, false]
const booleanList = extractCompletedValues.flat();
console.log(booleanList)
// every will return true if all the values in the Array are true else false
const hasOneNonCompletedItem = booleanList.every(Boolean)
console.log(hasOneNonCompletedItem); // will return false if there is atleast one false
Try this:
var selected_items = { 32: 1, 31: 1, }
var arrOb =Object.keys(selected_items );
var res = items.filter((item) =>arrOb.includes(item.id));
var flag = false ;
for(let i = 0 ;i<res.length;i++){
if(res[i].jobs.some(ele=>ele.completed===true)){
flag = true ;
break ;
}
}
if(flag){console.log("it contains completed true")}
else{console.log(false)}
Data Structure coming back from the server
[
{
id: 1,
type: "Pickup",
items: [
{
id: 1,
description: "Item 1"
}
]
},
{
id: 2,
type: "Drop",
items: [
{
id: 0,
description: "Item 0"
}
]
},
{
id: 3,
type: "Drop",
items: [
{
id: 1,
description: "Item 1"
},
{
id: 2,
description: "Item 2"
}
]
},
{
id: 0,
type: "Pickup",
items: [
{
id: 0,
description: "Item 0"
},
{
id: 2,
description: "Item 2"
}
]
}
];
Each element represents an event.
Each event is only a pickup or drop.
Each event can have one or more items.
Initial State
On initial load, loop over the response coming from the server and add an extra property called isSelected to each event, each item, and set it as false as default. -- Done.
This isSelected property is for UI purpose only and tells user(s) which event(s) and/or item(s) has/have been selected.
// shove the response coming from the server here and add extra property called isSelected and set it to default value (false)
const initialState = {
events: []
}
moveEvent method:
const moveEvent = ({ events }, selectedEventId) => {
// de-dupe selected items
const selectedItemIds = {};
// grab and find the selected event by id
let foundSelectedEvent = events.find(event => event.id === selectedEventId);
// update the found event and all its items' isSelected property to true
foundSelectedEvent = {
...foundSelectedEvent,
isSelected: true,
items: foundSelectedEvent.items.map(item => {
item = { ...item, isSelected: true };
// Keep track of the selected items to update the other events.
selectedItemIds[item.id] = item.id;
return item;
})
};
events = events.map(event => {
// update events array to have the found selected event
if(event.id === foundSelectedEvent.id) {
return foundSelectedEvent;
}
// Loop over the rest of the non selected events
event.items = event.items.map(item => {
// if the same item exists in the selected event's items, then set item's isSelected to true.
const foundItem = selectedItemIds[item.id];
// foundItem is the id of an item, so 0 is valid
if(foundItem >= 0) {
return { ...item, isSelected: true };
}
return item;
});
const itemCount = event.items.length;
const selectedItemCount = event.items.filter(item => item.isSelected).length;
// If all items in the event are set to isSelected true, then mark the event to isSelected true as well.
if(itemCount === selectedItemCount) {
event = { ...event, isSelected: true };
}
return event;
});
return { events }
}
Personally, I don't like the way I've implemented the moveEvent method, and it seems like an imperative approach even though I'm using find, filter, and map.
All this moveEvent method is doing is flipping the isSelected flag.
Is there a better solution?
Is there a way to reduce the amount of looping? Maybe events should be an object and even its items. At least, the lookup would be fast for finding an event, and I don't have to use Array.find initially. However, I still have to either loop over each other non selected events' properties or convert them back and forth using Object.entries and/or Object.values.
Is there more a functional approach? Can recursion resolve this?
Usage and Result
// found the event with id 0
const newState = moveEvent(initialState, 0);
// Expected results
[
{
id: 1,
type: 'Pickup',
isSelected: false,
items: [ { id: 1, isSelected: false, description: 'Item 1' } ]
}
{
id: 2,
type: 'Drop',
// becasue all items' isSelected properties are set to true (even though it is just one), then set this event's isSelected to true
isSelected: true,
// set this to true because event id 0 has the same item (id 1)
items: [ { id: 0, isSelected: true, description: 'Item 0' } ]
}
{
id: 3,
type: 'Drop',
// since all items' isSelected properties are not set to true, then this should remain false.
isSelected: false,
items: [
{ id: 1, isSelected: false, description: 'Item 1' },
// set this to true because event id 0 has the same item (id 2)
{ id: 2, isSelected: true, description: 'Item 2' }
]
}
{
id: 0,
type: 'Pickup',
// set isSelected to true because the selected event id is 0
isSelected: true,
items: [
// since this belongs to the selected event id of 0, then set all items' isSelected to true
{ id: 0, isSelected: true, description: 'Item 0' },
{ id: 2, isSelected: true, description: 'Item 2' }
]
}
]
One of the problems with the current solution is data duplication. You are basically trying to keep the data between the different items in sync. Instead of changing all items with the same id, make sure there are no duplicate items by using an approach closer to what you would find in a rational database.
Let's first normalize the data:
const response = [...]; // data returned by the server
let data = { eventIds: [], events: {}, items: {} };
for (const {id, items, ...event} of response) {
data.eventIds.push(id);
data.events[id] = event;
event.items = [];
for (const {id, ...item} of items) {
event.items.push(id);
data.items[id] = item;
}
}
This should result in:
const data {
eventIds: [1, 2, 3, 0], // original order
events: {
0: { type: "Pickup", items: [0, 2] },
1: { type: "Pickup", items: [1] },
2: { type: "Drop", items: [0] },
3: { type: "Drop", items: [1, 2] },
},
items: {
0: { description: "Item 0" },
1: { description: "Item 1" },
2: { description: "Item 2" },
},
};
The next thing to realize is that the isSelected property of an event is computed based on the isSelected property of its items. Storing this would mean more data duplication. Instead calculate it though a function.
const response = [{id:1,type:"Pickup",items:[{id:1,description:"Item 1"}]},{id:2,type:"Drop",items:[{id:0,description:"Item 0"}]},{id:3,type:"Drop",items:[{id:1,description:"Item 1"},{id:2,description:"Item 2"}]},{id:0,type:"Pickup",items:[{id:0,description:"Item 0"},{id:2,description:"Item 2"}]}];
// normalize incoming data
let data = { eventIds: [], events: {}, items: {} };
for (const {id, items, ...event} of response) {
data.eventIds.push(id);
data.events[id] = event;
event.items = [];
for (const {id, ...item} of items) {
event.items.push(id);
data.items[id] = item;
item.isSelected = false;
}
}
// don't copy isSelected into the event, calculate it with a function
const isEventSelected = ({events, items}, eventId) => {
return events[eventId].items.every(id => items[id].isSelected);
};
// log initial data
console.log(data);
for (const id of data.eventIds) {
console.log(`event ${id} selected?`, isEventSelected(data, id));
}
// moveEvent implementation with the normalized structure
const moveEvent = (data, eventId) => {
let { events, items } = data;
for (const id of events[eventId].items) {
items = {...items, [id]: {...items[id], isSelected: true}};
}
return { ...data, items };
};
data = moveEvent(data, 0);
// log after data applying `moveEvent(data, 0)`
console.log(data);
for (const id of data.eventIds) {
console.log(`event ${id} selected? `, isEventSelected(data, id));
}
// optional: convert structure back (if you still need it)
const convert = (data) => {
const { eventIds, events, items } = data;
return eventIds.map(id => ({
id,
...events[id],
isSelected: isEventSelected(data, id),
items: events[id].items.map(id => ({id, ...items[id]}))
}));
};
console.log(convert(data));
Check browser console, for better ouput readability.
I'm not sure if this answers solves your entire problem, but I hope you got something useful info out of it.
I would like to copy an array so as not to modify the original, and remove all selected: false from new array, and return this new array. The array is infinitely nested, and with infinite property names non of which are predictable, so this should be possible through iteration looking at the value of each property for Array.isArray(). While I can remove selected:false objects in the iteration, I fail to return the modified array back to the new array.
function failing to filter aliens. Also, function works in CodePen, but not in my code.
// sample nested data
var data = [
{
partyId: "animal-ID-001",
selected: false,
members: [
{
selected: false,
userId: "animal-user-3443"
},
{
selected: false,
userId: "animal-user-3444"
}
]
},
{
partyId: "benjamin-ID-002",
selected: true,
members: [
{
selected: true,
userId: "benjamin-user-5567",
teams: [
{
selected: true,
teamId: "team-benjamin-678"
},
{
selected: false,
teamId: "team-benjamin-3468"
}
]},
{
selected: false,
userId: "benjamin-user-55671"
}
]
},
{
partyId: "crystal-ID-003",
selected: true,
members: [
{
selected: true,
userId: "crystal-user-8567"
},
{
selected: true,
userId: "crystal-user-85671"
}
],
aliens: [
{
selected: false,
alienId: "crystal-alien-467"
},
{
selected: false,
alienId: "crystal-alien-230"
}
]
}
];
// remove selected false from array
// updated per suggestions
function updateState(arr) {
return arr.filter(obj => obj.selected ).map( obj => {
for (var prop in obj) {
if( Array.isArray( obj[prop] ) ) {
return { ...obj, [prop]: updateState( obj[prop] ) };
}
}
return { ...obj }
});
}
console.log( updateState( data ) );
Does something like this work for you?:
const removeNonselected = (x) =>
Array .isArray (x)
? x .reduce (
(all, item) => item .selected === false
? all
: all .concat (removeNonselected (item)),
[]
)
: typeof x == 'object'
? Object .fromEntries (
Object .entries (x) .map(([n, v]) => [n, removeNonselected(v)])
)
: x
const data = [{partyId: "animal-ID-001", selected: false, members: [{selected: false, userId: "animal-user-3443"}, {selected: false, userId: "animal-user-3444"}]}, {partyId: "benjamin-ID-002", selected: true, members: [{selected: true, userId: "benjamin-user-5567", teams: [{selected: true, teamId: "team-benjamin-678"}, {selected: false, teamId: "team-benjamin-3468"}]}, {selected: false, userId: "benjamin-user-55671"}]}, {partyId: "crystal-ID-003", selected: true, members: [{selected: true, userId: "crystal-user-8567"}, {selected: true, userId: "crystal-user-85671"}], aliens: [{selected: false, alienId: "crystal-alien-467"}, {selected: false, alienId: "crystal-alien-230"}]}];
console .log (removeNonselected (data))
console .log ('original data unchanged:')
console .log (data)
This handles three cases: where the data is an array, where it's an object, or where it's something else. For an array we keep only the selected ones (where selected is not false) and recurs on those values. For an object, we keep other values intact, but recur on array properties. Anything else we just return as is.
This does not remove a selected: false property of an object, only from within an array. It would not be much harder to add that, but it didn't seem to be in your requirements.
If you environment doesn't support Object.fromEntries, it's fairly easy to shim.
First .filter the original data, removing items with selected: false, then .map the result, and inside the callback, return the same object while .filtering the members property. Then
var data = [{
partyId: "animal-ID-001",
selected: false,
members: [{
selected: false,
userId: "animal-user-3443"
},
{
selected: false,
userId: "animal-user-3444"
}
]
},
{
partyId: "benjamin-ID-002",
selected: true,
members: [{
selected: true,
userId: "benjamin-user-5567"
},
{
selected: false,
userId: "benjamin-user-55671"
}
]
},
{
partyId: "crystal-ID-003",
selected: true,
members: [{
selected: true,
userId: "crystal-user-8567"
},
{
selected: true,
userId: "crystal-user-85671"
}
]
}
];
const updatedData = data
.filter(({ selected }) => selected)
.map(({ members, ...rest }) => ({
...rest,
members: members.filter(({ selected }) => selected)
}));
console.log(updatedData);
Try this simple one:
let selectedParties = data.filter(item => item.selected);
let partiesWithSelectedMembersOnly = selectedParties.map(item => {
return {
...item,
members: item.members.filter(member => member.selected)
};
});
Array.filter() returns new array, so you will not modify initial one.
Don't reinvent the wheel, check what functions you can use on arrays. Array.filter with recursion is perfect here:
var data=[{partyId:"animal-ID-001",selected:!1,members:[{selected:!1,userId:"animal-user-3443"},{selected:!1,userId:"animal-user-3444"}]},{partyId:"benjamin-ID-002",selected:!0,members:[{selected:!0,userId:"benjamin-user-5567"},{selected:!1,userId:"benjamin-user-55671"}]},{partyId:"crystal-ID-003",selected:!0,members:[{selected:!0,userId:"crystal-user-8567"},{selected:!0,userId:"crystal-user-85671"}]}];
function removeNonselected(arr) {
return arr.filter(obj => obj.selected).map(obj => {
if(obj.members) return { ...obj, members: removeNonselected(obj.members) };
else return { ...obj }
});
}
console.log(removeNonselected(data));