I'm creating a series of filters and it's currently "ugly code that works". I'd like to output all of the filters from an array, but I can't figure out how to assign a variable to an element's dataset attribute. (You'll see that the code is the exact same for every filter except for what they're filtering).
Can anyone help show me how I could achieve this?
function filterList() {
const itemsNode = document.querySelectorAll(".js-filterable");
const items = Array.from(itemsNode);
const filterBrand = document.querySelector(".js-filter-brand");
const filterState = document.querySelector(".js-filter-state");
const filterCity = document.querySelector(".js-filter-city");
const filterOwner = document.querySelector(".js-filter-owner");
const filtered = document.querySelector(".js-filtered");
let filterValue;
let results;
// Listen for filter changes
if (filterBrand) {
filterBrand.addEventListener("input", function(filtered, filterValue) {
filterValue = this.value;
if (filterValue === "all") {
let results = items;
outputResults(results);
} else {
let results = items.filter(item => item.dataset.brand === filterValue);
outputResults(results);
}
});
}
if (filterState) {
filterState.addEventListener("input", function(filtered, filterValue) {
filterValue = this.value;
if (filterValue === "all") {
let results = items;
outputResults(results);
} else {
let results = items.filter(item => item.dataset.state === filterValue);
outputResults(results);
}
});
}
if (filterCity) {
filterCity.addEventListener("input", function(filtered, filterValue) {
filterValue = this.value;
if (filterValue === "all") {
let results = items;
outputResults(results);
} else {
let results = items.filter(item => item.dataset.city === filterValue);
outputResults(results);
}
});
}
if (filterOwner) {
filterOwner.addEventListener("input", function(filtered, filterValue) {
filterValue = this.value;
if (filterValue === "all") {
let results = items;
outputResults(results);
} else {
let results = items.filter(item => item.dataset.owner === filterValue);
outputResults(results);
}
});
}
// Update filtered list
function outputResults(results) {
while (filtered.firstChild)
filtered.removeChild(filtered.firstChild);
results.forEach(function(result) {
filtered.appendChild(result);
});
}
}
It seems like you just want it to be DRYer:
function filterList() {
const itemsNode = document.querySelectorAll(".js-filterable");
const items = Array.from(itemsNode);
const filterClasses = ['brand', 'state', 'city', 'owner']
filterClasses.forEach(filterClass => {
const filtered = document.querySelector(`.js-filter-${filterClass}`);
if (filtered) {
filtered.addEventListener("input", function(filtered, filterValue) {
filterValue = this.value; // <-- why overwrite the param?
let results;
if (filterValue === "all")
results = items;
else
results = items.filter(item => item.dataset[filterClass] === filterValue);
outputResults(results);
});
}
})
}
// Update filtered list
function outputResults(results) {
const filteredItems = document.querySelector(".js-filtered");
while (filteredItems.firstChild)
filteredItems.removeChild(filteredItems.firstChild);
results.forEach(result => filteredItems.appendChild(result));
}
In particular notice:
filterClasses = ['brand', 'state', 'city', 'owner'] is used to iterate over the known class names
with item.dataset[filterClass] a variable is used to set the data value
I imagine you could begin with something like this:
const passThru = (x) => true;
// Return a filter function using the given property name
const filterPropName = (propName, filterValue) => filterValue === "all" ?
passThru :
({dataset}) => dataset[propName] === filterValue;
// ...
function createListener (propName) {
return () => {
const filterValue = this.value;
const results = items.filter(filterPropName(propName, filterValue));
outputResults(results);
}
}
if (filterBrand) {
filterBrand.addEventListener("input", createListener("brand"));
}
if (filterState) {
filterState.addEventListener("input", createListener("state"));
}
// Etc.
Related
In the following code I am trying to filter the data. However, I see in the algorithm that it loops through the first item in the userPassedFilter array and returns the filtered data but doesn't loop again to check the second item in the userPassedFilter array. I know this is happening because I call return true before it iterates through the entire userPassedFilter array. How can I assure that in the algorythm that it loops through all the items in the userPassedFilter before returning the final result?
**User Selected Filter**
let userPassedFilter = [{"phase":1},{"phase":2}]
**Data**
let dataList = [
{"voltage":35, "phase":2},
{"voltage":12, "phase":2},
{"voltage":12, "phase":3},
{"voltage":35, "phase":1}
]
**Filter Data Algorithm**
module.exports = {
filterData: (dataList, userPassedFilter)=>{
let filteredData = [];
filteredData = dataList.filter((data) =>{
for(let item of userPassedFilter){
for(let key in item){
if(data[key] === undefined || data[key] !== item[key])
return false
}
}
return true
})
return filteredData
}
}
filter over the dataList and return every object where some of its object properties are included in the userFilter.
const userPassedFilter = [{voltage:122},{phase:1},{phase:2},{voltage:44}];
const dataList = [
{voltage:44, phase:12},
{voltage:35, phase:2},
{voltage:12, phase:2},
{voltage:12, phase:3},
{voltage:35, phase:1},
{voltage:122, phase:156}
];
function filterData(dataList, userPassedFilter) {
return dataList.filter(obj => {
return userPassedFilter.some(inner => {
const [[ key, value ]] = Object.entries(inner);
return obj[key] === value;
});
});
}
console.log(filterData(dataList, userPassedFilter));
Introduce variable for each loop, so the result will match any of the elements in the userPassedFilter by matching all properties for the element.
let userPassedFilter = [{"phase":1},{"phase":2}];
let dataList = [{"voltage":35, "phase":2},{"voltage":12, "phase":2},{"voltage":12, "phase":3},{"voltage":35, "phase":1}];
const f = (dataList, userPassedFilter) => {
let filteredData = dataList.filter((data) => {
let filtered = true;
for (let item of userPassedFilter) {
let matched = true;
for (let key in item) {
if (data[key] === undefined || data[key] !== item[key]) {
matched = false;
break;
}
}
if (matched) {
filtered = false;
break;
}
}
return !filtered;
});
return filteredData;
};
console.log(f(dataList, userPassedFilter));
A shorter version:
let userPassedFilter = [{"phase":1},{"phase":2}];
let dataList = [{"voltage":35, "phase":2},{"voltage":12, "phase":2},{"voltage":12, "phase":3},{"voltage":35, "phase":1}];
const f = (dataList, userPassedFilter) => {
let filteredData = dataList.filter((data) => {
return userPassedFilter.some(item => Object.entries(item).every(([key, value]) => data[key] === value))
});
return filteredData;
};
console.log(f(dataList, userPassedFilter));
try adding a variable
filteredData = dataList.filter((data) => {
let myvar = true;
for(let item of userPassedFilter){
for(let key in item){
if(data[key] === undefined || data[key] !== item[key])
myvar = false
}
}
return myvar
})
try adding a variable to hold the values:
let userPassedFilter = [{
"phase": 1
}, {
"phase": 2
}]
let dataList = [{
"voltage": 35,
"phase": 2
},
{
"voltage": 12,
"phase": 2
},
{
"voltage": 12,
"phase": 3
},
{
"voltage": 35,
"phase": 1
}
]
function filterData(dataList, userPassedFilter) {
let filteredData = [];
filteredData = dataList.filter((data) => {
var throwVar = 0 //add this variable to hold the value
for (let item of userPassedFilter) {
for (let key in item) {
if (data[key] === undefined || data[key] !== item[key]) {
throwVar = 0
} else {
return true
}
}
}
if (throwVar == 0) {
return false
}
})
return filteredData
}
console.log(filterData(dataList, userPassedFilter))
I am using React. On click of a button, the following function is executed:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const updatedItem = updatedData.filter((ele) => ele.id === idValue)[0];
updatedItem.completed = true;
const newData = updatedData.filter((ele) => ele !== updatedItem);
newData.unshift(updatedItem);
return newData;
});
};
My data is an array of objects like this:
[{userId: 1, id: 2, title: "task 1", completed: true}, .....].
Basically I want to move the updated item to the start of the array. Is there any better solution for this?
updatedItem should not be mutated. And this string const newData = updatedData.filter((ele) => ele !== updatedItem); is not fine. You can do it like this :
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetItem = prevData.find((ele) => ele.id === idValue);
const updatedItem = { ...targetItem, completed: true };
const filteredData = prevData.filter((ele) => ele.id !== idValue);
return [updatedItem, ...filteredData];
});
};
Even better to reducing an extra filter:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetIndex = prevData.findIndex((ele) => ele.id === idValue);
return [{ ...prevData[targetIndex], completed: true }].concat(prevData.slice(0, targetIndex + 1)) .concat(
prevData.slice(targetIndex + 1)
)
});
};
First find index of updated element using Array.findIndex(), then remove the same element using Array.splice() and add it to front of the array.
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const index = updatedData.findIndex(obj => obj.id === idValue);
const [updatedItem] = updatedData.splice(index, 1);
updatedItem.completed = true;
updatedData.unshift(updatedItem);
return updatedData;
});
};
The simplest one with only one forEach.
const completeTaskHandler = idValue => {
setData(prevData => {
let updatedItem = {}, newData = [];
prevData.forEach((ele) => {
if (ele.id === idValue) {
updatedItem = ele;
updatedItem.completed = true;
} else {
newData.push(ele);
}
});
newData.unshift(updatedItem);
return newData;
});
};
i am getting below example data in this.selectedParameterContext.
Please try the below code.
deleteAllContextData(data) {
const newSelectedParameterContext = {
'records': []
};
this.selectedParameterContext.records.forEach(function (record) {
const newSelectedParameterValues = [];
record.values.forEach(function (parameter, parameterIndex) {
const isValid = data.data.values.find(function (item) {
return parameter.value === item.value && parameter.label === item.label;
});
if (isValid) {
newSelectedParameterValues.push(parameter);
}
});
newSelectedParameterContext.records.push({ ...record, 'values': newSelectedParameterValues });
});
this.selectedParameterContext = newSelectedParameterContext;
}
I have a 2 function,
inside my runConfigComplianceDeviceOnClick I am calling the getDeviceRunningCompliance function to get some other data and based on both the results I have to return an object,
But What I am observing my data from the getDeviceRunningCompliance (Axios request to get data) function is not returned and it executes next lines,
but when I see in the console value is updated,
How to handle this case,
how to wait for the function to execute and then go next javascript? wanted to deal with asynchronous data then proceed further to the next lines...
/**
* #param {*} graphTable
*/
const runConfigComplianceDeviceOnClick = graphTable => {
let selectedDevices = graphTable.dTable.store.state.selectedRowsData;
let paramSelectedDevices;
let filteredSelectedDevices;
let finalParam;
let supportedDevices = true;
let some = getDeviceRunningCompliance(selectedDevices);
console.log("getDeviceRunningCompliance some ", some)
if (some.length) {
filteredSelectedDevices = selectedDevices.map(function(device, index) {
console.log("getDeviceRunningCompliance some filteredSelectedDevices", some)
if (notSupportedFamilies.includes(device.series)) {
// console.log(i18n.no_support_available_for_aireos);
supportedDevices = false;
} else {
// console.log(i18n.label_configuration_data_not_available);
supportedDevices = true;
}
let valsss = some.find(x => x.id === device.id);
console.log("valsss ", valsss)
return {
id: device.id,
hostname: device.hostname,
val: device.complianceStoreStatus.complianceStatus,
collectionStatus: device.collectionStatus,
series: device.series,
supportedDevices: supportedDevices
};
});
finalParam = filteredSelectedDevices.filter(function(val, index) {
return val.supportedDevices && val.val === "NON_COMPLIANT"; // this should be enable
});
paramSelectedDevices = JSON.stringify(finalParam);
localStorage.setItem("selectedDevicesConfigSync", paramSelectedDevices);
if (selectedDevices.length !== finalParam.length) {
toast({
message: finalParam.length + i18n.device_out_of_sync_for_start_vs_run,
flavor: "warning",
label: i18n.toast_header_running_configuration
});
}
shell.router.push(`/dna/provision/configuration-compliance`);
}
};
const getDeviceRunningCompliance = (selectedDevices) => {
let self = this;
let deviceRunningComplaince = [];
selectedDevices.forEach((val, index) => {
let obj = {};
getComplianceDetails(val.id).then(data => {
const complianceDetailsData = data;
if (complianceDetailsData) {
// this.setState({
// complianceDetailsData: data
// });
let cardStatus;
let complianceApiDataForConfig =
complianceDetailsData && complianceDetailsData.filter(config => config.complianceType === "RUNNING_CONFIG");
cardStatus =
complianceApiDataForConfig && complianceApiDataForConfig.length && complianceApiDataForConfig[0].status;
obj.id = val.id;
obj.runningStatus = cardStatus;
deviceRunningComplaince.push(obj);
// return cardStatus;
}
});
// deviceRunningComplaince.push(obj);
});
return deviceRunningComplaince;
};
This is how I solved this issue. Please comment if we can do this better.
/**
* #param {*} graphTable
*/
const runConfigComplianceDeviceOnClick = graphTable => {
let selectedDevices = graphTable.dTable.store.state.selectedRowsData;
let paramSelectedDevices;
let filteredSelectedDevices;
let finalParam;
let supportedDevices = true;
getDeviceRunningCompliance(selectedDevices).then(function(some) {
if (some.length) {
filteredSelectedDevices = selectedDevices.map(function(device, index) {
console.log("getDeviceRunningCompliance some filteredSelectedDevices", some);
if (notSupportedFamilies.includes(device.series)) {
// console.log(i18n.no_support_available_for_aireos);
supportedDevices = false;
} else {
// console.log(i18n.label_configuration_data_not_available);
supportedDevices = true;
}
let valsss = some.find(x => x.id === device.id);
console.log("valsss ", valsss);
return {
id: device.id,
hostname: device.hostname,
val: device.complianceStoreStatus.complianceStatus,
collectionStatus: device.collectionStatus,
series: device.series,
supportedDevices: supportedDevices
};
});
finalParam = filteredSelectedDevices.filter(function(val, index) {
return val.supportedDevices && val.val === "NON_COMPLIANT"; // this should be enable
});
paramSelectedDevices = JSON.stringify(finalParam);
localStorage.setItem("selectedDevicesConfigSync", paramSelectedDevices);
if (selectedDevices.length !== finalParam.length) {
toast({
message: finalParam.length + i18n.device_out_of_sync_for_start_vs_run,
flavor: "warning",
label: i18n.toast_header_running_configuration
});
}
shell.router.push(`/dna/provision/configuration-compliance`);
}
});
};
const getDeviceRunningCompliance = selectedDevices => {
let promiseData = selectedDevices.map((val, index) => {
return getComplianceDetails(val.id).then(data => {
let obj = {};
const complianceDetailsData = data;
if (complianceDetailsData) {
let cardStatus;
let complianceApiDataForConfig =
complianceDetailsData && complianceDetailsData.filter(config => config.complianceType === "RUNNING_CONFIG");
cardStatus =
complianceApiDataForConfig && complianceApiDataForConfig.length && complianceApiDataForConfig[0].status;
obj.id = val.id;
obj.runningStatus = cardStatus;
return obj;
}
});
});
return Promise.all(promiseData);
};
I am trying to query a Firebase database. I have the following code, but it returns an empty list, when there is matching data.
loadData() {
this.firelist = this.af.database.list('/chat/', {
query: {
orderByChild: 'negativtimestamp'
}
}).map(items => {
const filtered = items.filter(item => {
item.memberId1 === this.me.uid;
});
return filtered;
});
// if me not in firelist then create new chat
if (this.me && this.me.uid) {
let chatFound: boolean = false;
console.log('this.firelist', this.firelist);
this.firelist.forEach(chatItems => {
console.log('chatItems', chatItems);
for (let i = 0; i < chatItems.length; i++) {
console.log('chatItems['+i+']', chatItems[i]);
let memberId1: string = chatItems[i].memberId1;
let memberId2: string = chatItems[i].memberId2;
if (memberId1 === this.me.uid || memberId2 === this.me.uid) {
chatFound = true;
break;
}
}
if (!chatFound) {
//this.createChat();
}
});
}
}
I think my problem is with the filter.
The following code creates a chat successfully:
createChat(img1: string, img2: string) {
this.af.database.list('/chat/').push({
memberId1: this.me.uid,
memberId2: this.you.uid,
img1: img1,
img2: img2,
displayName1: this.me.displayName,
displayName2: this.you.displayName,
lastMsg_text: 'todo',
timestamp: Date.now(),
negativtimestamp: -Date.now()
})
}
The following filter works:
this.firelist = this.af.database.list('/chat/', {
query: {
orderByChild: 'negativtimestamp'
}
}).map(items => {
const filtered = items.filter(
item => item.memberId1 === this.me.uid
);
return filtered;
});