Vanilla JavaScript search - how to add multiple fields? - javascript

This function is searching from a Json data the field "title".
Please, how can I modify this to include multiple fields, like: "tags", "author" etc.? Thanks!
document.addEventListener('DOMContentLoaded', function(event) {
const search = document.getElementById('search');
const results = document.getElementById('results');
let data = [];
let search_term = '';
fetch('/search.json')
.then(response => response.json())
.then(data_server => {
data = data_server;
});
search.addEventListener('input', event => {
search_term = event.target.value.toLowerCase();
showList();
});
const showList = () => {
results.innerHTML = '';
if (search_term.length <= 0) return;
const match = new RegExp(`${search_term}`, 'gi');
let result = data.filter(name => match.test(name.title));
if (result.length == 0) {
const li = document.createElement('li');
li.innerHTML = `No results found 😢`;
results.appendChild(li);
}
result.forEach(e => {
const li = document.createElement('li');
li.innerHTML = `${e.title}`;
results.appendChild(li);
});
};
});

change
let result = data.filter(name => match.test(name.title));
to
let result = data.filter(name => match.test(name.title) || match.test(name.tags) || match.test(name.auther));

It may be an idea to filter on all entries of the objects within the Array retrieved from the json.
Here's a minimal reproducable example, using Event Delegation.
See also
document.addEventListener(`click`, handle);
const data = getJSONFakeData();
function handle(evt) {
if (evt.target.id === `search`) {
return searchJSON();
}
}
function searchJSON() {
const resultsDiv = document.querySelector(`#results`);
resultsDiv.textContent = ``;
const nothingFound = isEmpty =>
resultsDiv.insertAdjacentHTML(
`beforeend`,
`<h3>${isEmpty
? `😢 No input`
: `No results found 😢`}</h3>` );
const term = document.querySelector(`#term`).value.trim();
if (term.length < 1) {
return nothingFound(true);
}
const re = new RegExp(term, `gi`);
// filter here
const results = data
.filter( entry => Object.entries(entry)
.find( ([, value]) => re.test(value) )
);
if (results.length) {
let elems = [];
results.forEach( result => {
const res = Object.entries(result)
.reduce( (acc, [key, value]) =>
acc.concat(`<i>${key}</i>: ${value};<br>`), ``);
elems.push(`<li>${res}</li>`);
});
return resultsDiv.insertAdjacentHTML(
`beforeend`,
`<ul>${elems.join(``)}</ul>`);
}
return nothingFound();
}
function getJSONFakeData() {
return [{
title: `title1`,
author: `author1`,
tags: `science, medicine`,
editor: `Springer Verlag`
},
{
title: `title2`,
author: `author2`,
tags: `automotive, engine`,
editor: `Elsevier`
},
{
title: `title3`,
author: `author3`,
tags: `programming, functional, loops`,
editor: `Elsevier`
},
];
}
body {
font: normal 12px/15px verdana, arial;
margin: 2em;
}
<input type="text" id="term" value="Elsevier">
<button id="search">Find</button>
<div id="results"></div>

Related

how to access a variable outside onClick function react

I'm trying to get list from the onClick function but I can't if there any solution, please.
here is my full code link
let abe = []
const click = (e) => {
const cityy = e.target.value
const checkUsername = obj => obj.city === cityy;
const result = abe.some(checkUsername)
if (!result) {
abe.push({ "city": cityy})
}
if (!e.target.checked) {
const indexe = abe.findIndex(p => p.city === cityy)
abe.splice(indexe, 1)
}
const simo = watch("simo")
let list = abe.map((list) => list.city).join(" , ")
}
Is the click function triggered in the first place? You have actually missed to show where the click function is used.
Here is an example. Looks like you have to store cities in the state.
const [citiesList, setCitiesList] = useState<string[]>([]);
const click = (e) => {
const cityy = e.target.value
const checkUsername = obj => obj.city === cityy;
const result = citiesList.some(checkUsername)
if (!result) {
setCitiesList(prevState => [...prevState, cityy]);
}
if (!e.target.checked) {
const cList = [...citiesList];
const indexe = cList.findIndex(p => p === cityy)
cList.splice(indexe, 1);
setCitiesList(cList);
}
const simo = watch("simo");
}

Run async/await function inside a reduce Javascript [duplicate]

This question already has answers here:
JavaScript array .reduce with async/await
(11 answers)
Closed 6 months ago.
I need to fetch values from another API using the guid inside this particular array, then group them together (hence I used reduce Javascript in this case)
However, I could not get those values sumEstimatedHours and sumWorkedHours as expected. Can someone suggest a method please?
export const groupProjectsByPM = (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = listOfProjects?.reduce(
(
group,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
here is an example of async/await inside reduce:
let's assume that we have an array of numbers
const arrayOfNumbers = [2,4,5,7,6,1];
We are going to sum them using reduce function:
const sumReducer = async () => {
const sum = await arrayOfNumbers.reduce(async (promisedSum, num) => {
const sumAcc = await promisedSum
// any promised function can be called here..
return sumAcc + num
}, 0)
console.log(sum)
}
So the trick is to remember to await the accumulator inside the reduce function
export const groupProjectsByPM = async (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = await listOfProjects?.reduce(
async (
promisedGroup,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
//accumulator in your case is group
const group = await promisedGroup;
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
Best of luck ...

Result won't update VAR

I am trying to run a query, inside AXIOS which gets data from a 3rd party URL. Then uses some of that data to search our mongoDB database.
However it seems it won't update var total = 0
While the query below does function correctly, the return result won't allow me to set that it to the query.
Promise.all(arr.forEach( async (id,index) => {
//(CODE REMOVED JUST TO GET THIS FUNCTION TO WORK)
const search = await geoLocation.find({
'location': {
'$geoWithin': {
'$box': [
[-35.2418503, -13.5076852], [112.8656697, 129.0020486]
]
}
}}).toArray();
total = search.length;
}));
See the full code below
var array = [];
var pointarray = []
var total = 0;
areas.forEach((id,index) => {
if(id.type == "Point"){
pointarray[index] = "N"+id.id;
}else{
array[index] = "R"+id.id;
}
});
var arraySearch = "https://nominatim.openstreetmap.org/lookup?osm_ids="+array.toString()+"&polygon_geojson=1&bbox=1&format=json";
var pointSearch = "https://nominatim.openstreetmap.org/lookup?osm_ids="+pointarray.toString()+"&polygon_geojson=1&bbox=0&format=json"
const requestOne = axios.get(arraySearch);
const requestTwo = axios.get(pointSearch);
axios.all([requestOne, requestTwo])
.then(axios.spread((...responses) => {
const responseOne = responses[0]
const responseTwo = responses[1]
/*
process the responses and return in an array accordingly.
*/
return [
responseOne.data,
responseTwo.data,
];
}))
.then(arr => {
Promise.all(arr.forEach( async (id,index) => {
//const middleIndex = id[index].boundingbox.length / 2;
//const firstHalf = id[index].boundingbox.splice(0, middleIndex);
//const secondHalf = id[index].boundingbox.splice(-middleIndex);
//res.send(secondHalf[0]);
const query = [{
$match: {
location: {
$geoWithin: {$box:[[Number(firstHalf[0]),Number(firstHalf[1])],[Number(secondHalf[0]),Number(secondHalf[1])]]
}
}
}
},{
$count: 'id'
}]
const search = await geoLocation.find({
'location': {
'$geoWithin': {
'$box': [
[-35.2418503, -13.5076852], [112.8656697, 129.0020486]
]
}
}}).toArray();
total = search.length;
// total = search.length;
// const search = geoLocation.aggregate(query).toArray.length;
}));
})
.catch(errors => {
console.log("ERRORS", errors);
})
.then(function () {
res.send(total);
});

Is there a better way to achieve this?

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;
});
};

how to get and display data from firebase realtime database?

I want to get data from the database. Then change them. And then display.
Please tell me how to solve this problem and why I can not do it.
Here is my code
let firebaseConfig = {...};
firebase.initializeApp(firebaseConfig);
let ref = firebase.database().ref('/data')
class DataTable {
constructor(parent) {
this.parent = parent;
}
buildTable(data) {
this.data = data;
const keys = Object.keys(data[0]);
console.log(keys)
let div = document.createElement('div');
let tab = document.createElement('table');
let tb = document.createElement('tbody');
const buildTableBody = () => {
for (let a of data) {
let tr = document.createElement('tr');
keys.forEach((key) => {
let td = document.createElement('td');
let tn = document.createTextNode(a[key])
td.appendChild(tn);
tr.appendChild(td);
});
tb.appendChild(tr);
}
tab.appendChild(tb);
div.appendChild(tab);
}
this.parent.appendChild(div);
buildTableBody()
}
}
const table = new DataTable(document.body);
table.buildTable(
ref.once("value").then((snap) => {
const data = snap.val()
data.map(i => {
let res = {
'#': Number(i.id),
'Name': i.name,
};
return Object.entries(res).reduce((memo, [key, value]) => {
if (value) {
return {
...memo,
[key]: value
}
} else {
return memo;
}
}, {})
})
}))
But it returns to me Promise {}proto: Promise[[PromiseStatus]]: "resolved"[[PromiseValue]]: undefined
The way you're trying to pass the data into buildTable doesn't work. If you put a breakpoint inside buildTable, you'll be able to see that.
The reason is that the data is loaded from Firebase asynchronously, and any code that needs the data has to be called from inside the once() callback. So you'll want to put the call to buildTable within that callback, like this:
ref.once("value").then((snap) => {
const data = snap.val()
let result = data.map(i => {
let res = {
'#': Number(i.id),
'Name': i.name,
};
return Object.entries(res).reduce((memo, [key, value]) => {
if (value) {
return {
...memo,
[key]: value
}
} else {
return memo;
}
}, {})
})
table.buildTable(result);
}))

Categories

Resources