I have an array object with several variables. The objects are created by reading data from firebase.
// Initialize cloud firestore database
let db = firebase.firestore();
//Create a class to store object data
class Data{
constructor(ID,ame, type, location, address, category) {
this.ID = ID;
this.type = type;
this.location = location;
this.address = address;
this.category = category;
}
}
//Get all documents in collection
db.collection("Basic_Data").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
Data_t = new Data(doc.ID,doc.data().type,doc.data().location,doc.data().address,doc.data().category);
all_data.push(Data_t);
});
});
I need to filter the objects based on certain conditions and if multiple filters are selected I need objects that satisfy all the conditions. I have no issues filtering that. After filtering, I try to consolidate values from the different filtering process. But some objects satisfy multiple conditions and are therefore included multiple times (not just twice). Thankfully, every object has a unique ID which I can use to filter the duplicates. But how do I check if an object with the unique ID is already present in the array?
//Filter data by user selection
function dataFilter() {
if ((document.getElementById("filter1-chkBox").checked) || (document.getElementById("filter2-chkBox").checked) || (document.getElementById("filter3-chkBox").checked) || (document.getElementById("filter4-chkBox").checked)) {
if (document.getElementById("filter1-chkBox").checked) {
temp_data_m = all_data.filter(function(info) {
return info.condition1 == true;
});
}
if (document.getElementById("filter2-chkBox").checked) {
temp_data_w = all_data.filter(function(info) {
return info.condition2 == true;
});
}
if (document.getElementById("filter3-chkBox").checked) {
temp_data_d = all_data.filter(function(info) {
return info.condition3 == true;
});
}
if (document.getElementById("filter4-chkBox").checked) {
temp_data_h = all_data.filter(function(info) {
return info.condition4 == true;
});
}
//Consolidate all the filter results
temp_data = temp_data_m;
if (temp_data_m.length != 0) {
temp_data_m = [];
}
if (temp_data_w.length != 0) {
temp_data = temp_data.concat(temp_data_w);
temp_data_w = [];
}
if (temp_data_d.length != 0) {
temp_data = temp_data.concat(temp_data_d);
temp_data_d = [];
}
if (temp_data_h.length != 0) {
temp_data = temp_data.concat(temp_data_h);
temp_data_h = [];
}
//Remove duplicates
temp_data.forEach((info) => {
if (!filtered_data.ID.includes(info.ID)) {
filtered_data.push(info);
}
});
} else {
filtered_data = temp_data;
}
}
I am trying to use forEach() and includes() to remove duplicates but I can't access the variable 'ID' from my array. How do I check the ID of all existing elements in the array?
Did you try to use filter()?
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
Related
I'm trying to create a chrome extension, but I am having some trouble updating my DB.
In the code below I am using index.get to the the object that contains a certain value. If such an object doesn't exist I will create a new one, which works just fine.
But if the DB contains an object with the specified value, I want to append a new object to an array (allMessages) that is inside the object I searched for. The details doesn't really matter in this case.
What is important is to find out if the way I'm adding this new obj to the array (allMessages) is a valid way of updating the database.
records.forEach((person) => {
console.log("here1");
const index = objectStore.index("urlKeyValue");
let search = index.get(person.urlKeyValue);
search.onsuccess = function (event) {
if (search.result === undefined) {
// no record with that key
let request = objectStore.add(person);
request.onsuccess = function () {
console.log("Added: ", person);
};
} else {
// here I'm iterating an array that is inside the obj I searched for,
// and then checking if the key for that array matches **theUserId**
for (userObj of event.target.result.allMessages) {
if (theUserId == Object.keys(userObj)) {
// is this part correct. Is it possible to update the DB this way?
let objToAdd1 = {
time: person.allMessages[0][theUserId][0].time,
msg: person.allMessages[0][theUserId][0].msg,
};
let currentObj = userObj[theUserId];
let updatedObj = currentObj.push(objToAdd1);
}
}
)}
Using objectStore.openCursor you can update only part of the record.
The following updates only book prices.
const transaction = db.transaction("books", "readwrite");
const objectStore = transaction.objectStore("books");
records = [{ id: "kimetu", price: 600 }];
records.forEach((book) => {
const index = objectStore.index("id");
const search = index.get(book.id);
search.onsuccess = () => {
if (search.result === undefined) {
const request = objectStore.add(book);
request.onsuccess = () => {
console.log("Added: ", book);
};
} else {
const request = objectStore.openCursor(IDBKeyRange.only(book.id));
request.onsuccess = () => {
const cursor = request.result;
if (cursor) {
cursor.value.price = 1000;
const updateRequest = cursor.update(cursor.value);
updateRequest.onsuccess = () => {
console.log("Updated: ", cursor.value.price);
};
cursor.continue();
}
};
}
}
});
I have an object productCounts
[{provisioned=2.0, product=str1, totalID=1.0},
{product=str2, provisioned=4.0, totalID=3.0},
{provisioned=6.0, product=str3, totalID=5.0}]
I have an array uniqueProduct
[str1, str2, str3, str4]
I am then looping a dataset to get the totalID count, add it to the product's totalID but if it doesn't exist, push it to the object.
var countID = 0;
uniqueProduct.forEach(
currentproduct => {
countID = 0;
for (var i = 0; i < shtRng.length; ++i) {
if (shtRng[i][ProductCol].toString() == currentproduct) { // && shtRng[i][IDcol].toString().length>4){
countID++;
}
}
if (countID == 0) {
return;
}
console.log(currentproduct + ": " + countID);
}
)
This works perfectly to return the countID per product in uniqueProduct
Rather than logging the result, I would like to add it to the object like this... If the current unique product is not in the productCounts object, add it.
let obj = productCounts.find((o, i) => {
if (o.product == currentproduct) {
productCounts[i] = { product: currentproduct, totalID: productCounts[i].totalID+countID, provisioned: productCounts[i].provisioned };
return true;
} else {
productCounts.push({ product: currentproduct, totalID: countID, provisioned: 0 });
return true;
}
});
In my head, this should work but it appears to skip some records or add the product multiple times. How do I add to the object correctly?
Expected output is the object to be something similar to:
[{provisioned=2.0, product=str1, totalID=35.0},
{product=str2, provisioned=4.0, totalID=8.0},
{provisioned=6.0, product=str3, totalID=51.0},
{provisioned=6.0, product=str4, totalID=14.0}]
The argument to find() is a function that returns a boolean when the element matches the criteria. The if statement should use the result of this, it shouldn't be in the condition function.
let obj = productCounts.find(o => o.product == currentProduct);
if (obj) {
obj.totalId += countID;
} else {
productCounts.push(productCounts.push({ product: currentproduct, totalID: countID, provisioned: 0 });
}
BTW, your life would be easier if you used an object whose keys are the product names, rather than an array of objects. You can easily turn the array of objects into such an object:
let productCountsObj = Object.fromEntries(productCounts.map(o => [o.product, o]));
if (currentProduct in productCountsObj) {
productCountsObj[currentProduct].totalID += countID;
} else {
productCountsObj[currentProduct] = { product: currentproduct, totalID: countID, provisioned: 0 };
}
When I try to search for the title of a product, it only shows results if it is either the exact words, one word, or a combination of consecutive words.
For example, if I'm trying to find the product blue flying car, the product does not show if I try searching for blue car. Basically I want my search to be fixed to accept different combinations. How can I do this?
var item_xlsx = './assets/data/products.xlsx';
var all_items = [];
var all_cats = [];
var subcats = [];
var catQuery = '';
var subCatQuery = '';
var titleQuery = '';
function load_data() {
// get query parameters
catQuery = get_param('cat');
subCatQuery = get_param('subcat');
titleQuery = get_param('searchtext');
$('#searchtext').val(titleQuery);
$('#cat').val(catQuery);
fetch(item_xlsx).then(function (res) {
if (!res.ok) throw new Error("fetch failed");
return res.arrayBuffer();
}).then(function (ab) {
var data = new Uint8Array(ab);
var workbook = XLSX.read(data, { type: "array" });
var first_sheet_name = workbook.SheetNames[0];
var worksheet = workbook.Sheets[first_sheet_name];
all_items = XLSX.utils.sheet_to_json(worksheet, { raw: true });
populate_cats();
populate_subcats(catQuery);
render_category_form();
render_search_form(catQuery, titleQuery, all_items);
render_search_summary(catQuery, titleQuery, all_items);
render_search_results(catQuery, titleQuery, all_items);
});
}
function get_param(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
/* Filter items and render search results */
function render_search_results(cat, query, items) {
let results = all_items.filter(function (value, index, self) { // filter by cat
if (!catQuery || catQuery === "") {
return true;
} else {
return value.cat == catQuery;
}
}).filter(function (value, index, self) { // filter by subcat
if (!subCatQuery || subCatQuery === "") {
return true;
} else {
return value.subcat == subCatQuery;
}
}).filter(function (value, index, self) { // filter by query
if (!titleQuery || titleQuery === "") {
return true;
} else {
var regex = new RegExp(titleQuery, "i");
return value.name.search(regex) != -1 || value.audience.search(regex) != -1;
}
})
Try to filter for each word in search query - this way the results will only contain these items which contain all the words searched.
Searching function example for tests:
// setting the test array values
var all_items = ['abba the cure metall', 'abba the best songs', 'blue car abba', 'moose egipt sand sun abba', 'sunny day the company', 'abba songs', 'egiptian culture songs', 'blue sunny skies', 'singing songs when sky is blue', 'in a car with abba', 'in a car with moose', 'moose is blue', 'matallic moose', 'best songs while driving a car', 'metall skies of egipt', 'sing a song with abba'];
// converting the array into arr of objects similar to those in your code
$.each(all_items, function(i,v){
all_items[i]= {name: v, audience: ''};
});
// display all elements
$.each(all_items, function(i,v){ $('#namesDiv').append( $('<div/>').text(v.name) ); });
//
$('form').submit(function(e){
e.preventDefault();
// setting the test query from input element
var titleQuery = $('#search').val();
// filter by query
var queryParts = titleQuery.split(/[^\w]+/g); // split the keywords
var result_items = $.extend(true, [], all_items); // copy all items to result array
for(var i=0; i<queryParts.length; i++){ // iterate through keywords
var regex = new RegExp(queryParts[i], "i");
// check if current keyword is a part of name if not remove the item from result array
result_items = result_items.filter(function (value, index, self) {
return regex.test(value.name) || regex.test(value.audience);
});
}
// show search results in console
console.clear();
console.info('found '+result_items.length+' items:');
console.log(JSON.stringify(result_items));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form><input id='search' value="song abba"><button type="submit">search</button></form>
<div id="namesDiv" style="padding-bottom:5em;"></div>
The searching code above when nested in your function will look like this:
/* Filter items and render search results */
function render_search_results(cat, query, items) {
var results = all_items.filter(function (value, index, self) { // filter by cat
if (!catQuery || catQuery === "") {
return true;
} else {
return value.cat == catQuery;
}
}).filter(function (value, index, self) { // filter by subcat
if (!subCatQuery || subCatQuery === "") {
return true;
} else {
return value.subcat == subCatQuery;
}
});
if (titleQuery) { // filter by query
var queryParts = titleQuery.split(/[^\w]+/g); // split the keywords from query
for(var i=0; i<queryParts.length; i++){ // iterate through keywords
var regex = new RegExp(queryParts[i], "i");
// check if current keyword is a part of name or audience if not remove the item from results array
results = results.filter(function (value, index, self) {
return regex.test(value.name) || regex.test(value.audience);
});
}
}
If you need to search whole words only - just play with regex - the above solution includes sub-string matches.
There is a complex object and based on an array which is given as an input I need to modify its properties. Illustration is shown below. If the "field" is same , add them to "or" array .If its different "field" add them to "and" array along with its "value". I am using Set to get keys from both source and input and using them to group based on its keys. Also whenever there are duplicates .ie., suppose the "filterObj" already has the same (field, value) pair. Be it in "and" or inside "or",Then don't add it in the final object
Sandbox: https://codesandbox.io/s/optimistic-mirzakhani-pogpw-so-dpvis
There is a TestCases file in the sandbox which its needs to pass
let filterObj = {
feature: "test",
filter: {
and: [{ field: "field2" }]
}
};
let obj = [{ field: "field2", value: "3" }];
let all_filters = [];
if (filterObj.filter.and && filterObj.filter.and.hasOwnProperty("or")) {
all_filters = [...filterObj.filter.and.or];
} else if (filterObj.filter.and) {
all_filters = [...filterObj.filter.and];
}
const all_objs = [...obj, ...all_filters];
const uniqKeys = all_objs.reduce(
(acc, curr) => [...new Set([...acc, curr.field])],
[]
);
const updateItems = uniqKeys.map(obj => {
const filter_items = all_objs.filter(item => item.field === obj);
let resultObj = {};
if (filter_items && filter_items.length > 1) {
resultObj.or = [...filter_items];
} else if (filter_items && filter_items.length === 1) {
resultObj = { ...filter_items[0] };
}
return resultObj;
});
var result = { ...filterObj, filter: { and: [...updateItems] } };
console.log(result);
Try it.
I redid the implementation, it happened more universally.
Parses any filters according to your algorithm that it finds.
All test cases are worked.
Sandbox link: https://codesandbox.io/s/optimistic-mirzakhani-pogpw-so-i1u6h
let filterObj = {
feature: "test",
filter: {
and: [
{
field: "field1",
value: "2"
}
]
}
};
let obj = [
{
field: "field1",
value: "2"
},
{
field: "field1",
value: "1"
}
];
var FilterController = function(filter) {
var self = this;
self.filter = filter;
// encapsulated map of objects by fields
var storeMap = {};
// counter of objects
var counter = 0;
var tryPutObjectToMap = function(object) {
if (typeof object === "object") {
// get type for grouping
var objectType = self.getObjectGroupType(object);
if (objectType !== null) {
// cheack have group
if (!storeMap.hasOwnProperty(objectType)) {
storeMap[objectType] = [];
}
var duplicate = storeMap[objectType].find(function(sObject) {
return self.getObjectValue(sObject) === self.getObjectValue(object);
});
// check duplicate
if (duplicate === undefined) {
counter++;
storeMap[objectType].push(object);
} else {
// TODO: Handle duplicates
}
} else {
// TODO: handle incorrect object
}
}
};
// get filter structure from map
var getFilterStructureFromMap = function() {
var result = {};
// check exists root filter and filed if have objects
if (counter > 0) {
result["and"] = [];
}
for (var key in storeMap) {
if (storeMap.hasOwnProperty(key)) {
var array = storeMap[key];
if (array.length > 1) {
result["and"].push({
// clone array
or: array.slice()
});
} else {
result["and"].push(array[0]);
}
}
}
return result;
};
// rewrite and get current filter
// if you need^ create new object for result
self.rewriteAndGetFilter = function() {
self.filter.filter = getFilterStructureFromMap();
return self.filter;
};
// not prototype function for have access to storeMap
self.putObjects = function(objects) {
if (Array.isArray(objects)) {
// recursive push array elements
objects.forEach(element => self.putObjects(element));
// handle array
} else if (typeof objects === "object") {
// handle object
if (objects.hasOwnProperty("and") || objects.hasOwnProperty("or")) {
for (var key in objects) {
//no matter `or` or `and` the same grouping by field
// inner object field
if (objects.hasOwnProperty(key)) {
self.putObjects(objects[key]);
}
}
} else {
// filters props not found, try push to store map
tryPutObjectToMap(objects);
}
} else {
// TODO: Handle errors
}
};
if (self.filter.hasOwnProperty("filter")) {
// put and parse current objects from filter
self.putObjects(self.filter.filter);
}
};
// function for grouping objects.
// for you get filed name from object.
// change if need other ways to compare objects.
FilterController.prototype.getObjectGroupType = function(obj) {
if (typeof obj === "object" && obj.hasOwnProperty("field")) {
return obj.field;
}
return null;
};
// get object value
FilterController.prototype.getObjectValue = function(obj) {
if (typeof obj === "object" && obj.hasOwnProperty("value")) {
return obj.value;
}
return null;
};
var ctrl = new FilterController(filterObj);
ctrl.putObjects(obj);
var totalFilter = ctrl.rewriteAndGetFilter();
console.log(totalFilter);
console.log(JSON.stringify(totalFilter));
EDIT 1
I did not change the logic; I made a function based on it.
let filterObj = {
feature: "test",
filter: {
and: [
{
field: "field1",
value: "2"
}
]
}
};
let obj = [
{
field: "field1",
value: 2
},
{
field: "field1",
value: "1"
}
];
function appendToFilter(filter, inputObjects) {
var storeMap = {};
var counter = 0;
var handlingQueue = [];
// if filter isset the appen to handling queue
if (filter.hasOwnProperty("filter")) {
handlingQueue.push(filter.filter);
}
// append other object to queue
handlingQueue.push(inputObjects);
// get first and remove from queue
var currentObject = handlingQueue.shift();
while (currentObject !== undefined) {
if (Array.isArray(currentObject)) {
currentObject.forEach(element => handlingQueue.push(element));
} else if (typeof currentObject === "object") {
if (currentObject.hasOwnProperty("and") || currentObject.hasOwnProperty("or")) {
for (var key in currentObject) {
if (currentObject.hasOwnProperty(key)) {
handlingQueue.push(currentObject[key]);
}
}
} else {
// TODO: append fild exists check
if (currentObject.field) {
if (!storeMap.hasOwnProperty(currentObject.field)) {
storeMap[currentObject.field] = [];
}
var localValue = currentObject.value;
// check duplicate
if (storeMap[currentObject.field].find(object => object.value === localValue) === undefined) {
counter++;
storeMap[currentObject.field].push(currentObject);
}
}
}
}
currentObject = handlingQueue.shift();
}
// create new filter settings
var newFilter = {};
// check exists root filter and filed if have objects
if (counter > 0) { newFilter["and"] = []; }
for (var storeKey in storeMap) {
if (storeMap.hasOwnProperty(storeKey)) {
var array = storeMap[storeKey];
if (array.length > 1) {
newFilter["and"].push({
// clone array
or: array.slice()
});
} else {
newFilter["and"].push(array[0]);
}
}
}
filter.filter = newFilter;
}
// update filterObj
appendToFilter(filterObj, obj);
console.log(filterObj);
EDIT 2,3 (UPDATED)
With others objects support.
export function appendToFilter(filter, inputObjects) {
var storeMap = {};
var others = [];
var counter = 0;
var handlingQueue = [];
// if filter isset the appen to handling queue
if (filter.hasOwnProperty("filter") && filter.filter.hasOwnProperty("and")) {
handlingQueue.push(filter.filter.and);
}
// append other object to queue
handlingQueue.push(inputObjects);
// get first and remove from queue
var currentObject = handlingQueue.shift();
while (currentObject !== undefined) {
if (Array.isArray(currentObject)) {
currentObject.forEach(element => handlingQueue.push(element));
} else if (typeof currentObject === "object") {
if (
currentObject.hasOwnProperty("and") ||
currentObject.hasOwnProperty("or")
) {
for (var key in currentObject) {
if (currentObject.hasOwnProperty(key)) {
handlingQueue.push(currentObject[key]);
}
}
} else {
// TODO: append fild exists check
if (currentObject.field) {
if (!storeMap.hasOwnProperty(currentObject.field)) {
storeMap[currentObject.field] = [];
}
var localValue = currentObject.value;
// check duplicate
if (
storeMap[currentObject.field].find(
object => object.value === localValue
) === undefined
) {
counter++;
storeMap[currentObject.field].push(currentObject);
}
} else {
// handle others objects^ without field "field"
counter++;
others.push(currentObject);
}
}
}
currentObject = handlingQueue.shift();
}
// create new filter settings
var newFilter = {};
// check exists root filter and filed if have objects
if (counter > 0) {
newFilter["and"] = [];
}
for (var storeKey in storeMap) {
if (storeMap.hasOwnProperty(storeKey)) {
var array = storeMap[storeKey];
if (array.length > 1) {
newFilter["and"].push({
// clone array
or: array.slice()
});
} else {
newFilter["and"].push(array[0]);
}
}
}
// Append others to result filter
others.forEach(other => newFilter["and"].push(other));
filter.filter = newFilter;
}
I have an array like this
var userdata = [
{"id":1,"gender":"M","first":"John","last":"Smith","city":"Seattle, WA","status":"Active"},
{"id":2,"gender":"F","first":"Kelly","last":"Ruth","city":"Dallas, TX","status":"Active"},
{"id":3,"gender":"M","first":"Jeff","last":"Stevenson","city":"Washington, D.C.","status":"Active"},
{"id":4,"gender":"F","first":"Jennifer","last":"Gill","city":"Seattle, WA","status":"Inactive"}
]
I need to filter this array on some conditions. The form of these conditions are like this.
var search_object = {gender:"M",city:"Seattle, WA"}
// Gender = M and city = 'Seattle, WA'
var search_object1 = {gender:"M"}
var search_object2 = {city:"Seattle, WA"}
// This is same as above
var search_array = {status:["Active","Inactive"]}
// Status Active or Inactive
var search_array = [{status:"Active"},{status:"Inactive"}]
// Same as above
var search_object1 = {gender:"F"}
var search_array = [{status:"Active"},{status:"Inactive"}]
//Gender = F and status = Active or Inactive
var search_object = {gender:"F"}
var search_array = [{status:["Active","Inactive"]}]
// same as above
I have tried looping but failed. Please help or suggest or provide some proper links to get help.
The following code covers all the cases you mentioned.
function search(searchObj, data) {
if(searchObj instanceof Array) {
return data.reduce(function(prev, current, index, array) {
return prev.concat(search(current, data));
}, []);
} else {
var results = data.filter(function(el) {
for(var prop in searchObj) {
if(searchObj[prop] instanceof Array) {
if(searchObj[prop].indexOf(el[prop]) == -1) {
return false;
}
} else
if(el[prop] !== searchObj[prop]) {
return false;
}
}
return true;
});
return results;
}
};
search(search_object, userdata);
Here is the working example in JSFiddle.
And here are some links to the functions I've used above:
Array.prototype.reduce()
Array.prototype.concat()
Array.prototype.filter()
Array.prototype.indexOf()
Just what RGraham said in the comments, you can use the filter function on arrays.
var search_object = {gender:"M",city:"Seattle, WA"};
var filtered = userdata.filter(function(obj){
return (obj.gender === search_object && obj.city === search_object.city)
});
filtered[0];//Array with objects that return true;