Javascript object as a type of mini database? - javascript

Is it possible to use a JavaScript object as a type of mini database? I often find myself needing a kind of database structure when I'm coding in JS but it feels like overkill to use an actual database like MySQL (or similar).
As an example, let's say I need to structure this data as a JS object:
Object idea: Stuff to sell
Items to sell: The junk in the garage
Object structure: List all items including item name, item condition, and item value
In order to make this into a JS object I would maybe write:
var stuffToSell = {};
Then my first item would maybe look like:
var stuffToSell = {
item : "Coffee Maker",
condition : "Good",
price : 5
};
Now to me this seems like I'm on the right track, until I come to add another item and I end up having to use the properties item, condition, and price again in the same JS object — which feels wrong? — or is it?? At this point my brain keeps shouting the word "ARRAY!" at me but I just can't see how I can use an array inside the object, or an object inside an array to achieve what I want.
My end goal (in this simplified example) is to be able to then use object-oriented syntax to be able to access certain items and find out specific information about the item such as price, condition etc. For example if I want to know the price of the "coffee maker" I would like to write something like:
stuffToSell["coffee maker"].price
...and then the result above should be 5.
I feel like I'm on the right track but I think I'm missing the array part? Could someone please tell me what I'm missing or maybe what I'm doing completely wrong! And also if it is wrong to have duplicate property names in the same JS object? For example, is it okay to have:
var stuffToSell = {
item : "Coffee Maker",
price : 5,
item : "Mountain Bike",
price : 10,
item : "26 inch TV",
price : 15
};
...it seems wrong because then how does JS know which price goes with which item??
Thanks in advance :)

You're definitely on the right track!
A lot of people will refer to what you're talking about as a hash.
Here's my suggested structure for you:
var store = {
coffee_maker: {
id: 'coffee_maker',
description: "The last coffee maker you'll ever need!",
price: 5,
},
mountain_bike: {
id: 'mountain_bike',
description: 'The fastest mountain bike around!',
price: 10,
},
tv: {
id: 'tv',
description: 'A big 26 inch TV',
price: 15,
},
}
Having a structure like that will let you do this:
store.mountain_bike.price // gives me 10
Need an array instead, say for filtering or looping over?
Object.keys gives you an Array of all the object's keys in the store ['coffee_maker', 'mountain_bike', 'tv']
// Now we just have an array of objects
// [{id: 'coffee_maker', price: 5}, {id: 'mountain_bike', price: 10} ...etc]
var arr = Object.keys(store).map(el => store[el])
Need to just filter for items that are less than 10?
This will give us an array of products less than 10:
// gives us [{id: 'coffee_maker', price: 5}]
var productsUnder10 = arr.filter(el => el.price < 10)
These techniques can be chained:
var productsOver10 = Object.keys(store)
.map(el => store[el])
.filter(el => el.price > 10)
Need to add a product?
store['new_product'] = {
id: 'new_product',
description: 'The Newest Product',
price: 9000,
}
Here's another way, which would be good to start getting used to.
This is a 'safe' way to update the store, read up on immutability in javascript to learn about it
store = Object.assign({}, store, {
'new_product': {
id: 'new_product',
description: 'The Newest Product',
price: 9000,
}
})
...and another way, that you should also read up on and start using:
This is the object spread operator, basically just an easier way to work with immutable structures
store = {
...store,
'new_product': {
id: 'new_product',
description: 'The Newest Product',
price: 9000,
}
}
Resources
JavaScript Arrow Functions
Object and Array Spread Syntax
Immutable Javascript using ES6 and beyond

You can actually use json or create an array of objects.If using a separate file to store the objects, first load the file. Use array filter method to get an new array which matches the filter condition , like you want to get the item with id 1. This will return an array of objects.
var dict = [{
'id': 1,
'name': 'coffee-mug',
'price': 60
},
{
'id': 2,
'name': 'pen',
'price': 2
}
]
function getItemPrice(itemId) {
var getItem = dict.filter(function(item) {
return item.id === itemId
});
return getItem[0].price;
}
console.log(getItemPrice(1))

JSON objects don't support repeated keys, so you need to set unique keys.
Put an id as your key to group your items:
var stuffToSell = {
'1': {
item: "Coffee Maker",
price: 5
},
'2': {
item: "Mountain Bike",
price: 10
}
.
.
.
}
Now you can access the item's price very fast.
Look at this code snippet (Known Ids)
var stuffToSell = {
'1': {
item: "Coffee Maker",
price: 5
},
'2': {
item: "Mountain Bike",
price: 10
},
'3': {
item: "26 inch TV",
price: 15
}
};
let getPrice = (id) => stuffToSell[id].price;
console.log(getPrice('1'));
See? the access to your items it's fast and your code follows a readable structure.
Look at this code snippet (Item's name as key)
var stuffToSell = {
'Coffee Maker': {
price: 5
},
'Mountain Bike': {
price: 10
},
'26 inch TV': {
price: 15
}
};
let getPrice = (id) => stuffToSell[id].price;
console.log(getPrice('Coffee Maker'));
Look at this code snippet (Item's name: price)
var stuffToSell = {
'Coffee Maker': 5,
'Mountain Bike': 10,
'26 inch TV': 15
};
let getPrice = (id) => stuffToSell[id];
console.log(getPrice('Coffee Maker'));

Related

Javascript arrays how to print sorted elements

I have a question about arrays. I'm new to javascript and I'm writing a program that has a function where it filters 20 elements of an array by category. That is, I have 3 buttons where, by clicking on one of them, the function is turned on and it starts displaying the filtered elements. Please tell me how can this be done? I have already tried a bunch of ways, but in the end nothing happened, although I think that I made a mistake somewhere.
array:
window.products = [
{
id: "i8",
title: "Iphone 8",
description:
"The iPhone 8 ",
price: 19900,
discontinued: false,
categories: ["c1"]
},
{
id: "i10",
title: "Iphone X",
description: "Iphone 10",
price: 39900,
discontinued: false,
categories: ["c1"]
},
{
id: "i11",
title: "Iphone 11",
description: "The iPhone 11 ",
price: 69900,
discontinued: false,
categories: ["c1"]
};
my function
function myFunction() {
document.getElementById("selected-category").innerHTML = "Iphones";
document.getElementById("category-products").innerHTML = "";
for (i = 0; i < 20; i++) {
document.getElementById("category-products").innerHTML = window.id;
const found = window.products.find(window.products.categories == "c1");
console.log(found);
}
}
part html code with button
<button onclick="myFunction()">Iphones</button>
First, you have syntax error in your windows.products = [ ... };. It's not closed properly (you need a ] before the };.
Then, the find method needs to be passed a function that processes an element of the Array. What you tried window.products.categories == "c1" evaluates to false, because the property categories does not exist in the window.products array. You get undefined on the left hand side and a string on the right hand side, so it's always false. You'd get "false is not a function".
Examples of using find() with a function:
const found = window.products.find( element => element.categories == "c1" );
or
const found = window.products.find( function(element) {
return element.categories == "c1"
});
But then:
The above == "c1" isn't what you should use, because it only matches due to type coercion from an array to a string, and only matches when the categories has only "c1" and no other elements. You should use includes method.
"find" will only give you one matching product. Use "filter" to find all matching ones.
If you only need to search for one key "c1", you can use
const found = window.products.filter( product=> product.categories.includes("c1")); and for two keys "c1" and "c2": const found = window.products.filter( product => product.categories.includes("c1") && product.categories.includes("c2"));
But I don't think you should use the above, because you should handle the case where the user searches for multiple keys.
const searchList = ['c1', 'c2', 'c3']
const found = window.products.filter ( function( product) {
//returns true if element.categories contains all of 'c1', 'c2' and 'c3'
return searchList.every( searchKey => product.categories.includes(searchKey)) ;
})
You can also do the search in one line, but may be harder to read :
const searchList = ['c1', 'c2', 'c3']
const found = window.products.filter ( product => searchList.every( searchKey => product.categories.includes(searchKey)) );

Javascript findindex performance issue

I am having an array as like this
var projectItemList=[{
"id":1,
"projectid":"a0I3l00001YiS67EAF",
"date":"2021-06-18",
"task":"",
"starttime":"",
"endtime":"",
"description":"",
"hours":"",
"projectname":"MyCOmp1",
"computedclass":"lookupclass1"
}, {
"id":1,
"projectid":"a0I3l00001YiS67EAF",
"date":"2021-06-18",
"task":"",
"starttime":"",
"endtime":"",
"description":"",
"hours":"",
"projectname":"Myproject",
"computedclass":"lookupclass1"
}];
and this array grows with n number of rows and I am pushing the dynamic constructed rows to the above array by incrementing the id.
and depending on keystroke I am changing the description of objects by finding the index and I am using this method to find the index
findIndex(id) {
return this.projectItemList.findIndex((obj => obj.id == id));
}
this.projectItemList[this.findIndex(id)].description ='xxxxxxx'
If the array length is 5 then things are fine. If the array grows more than 10, say 50 or 100
then its taking some delay to find the index and causing the issue.
I tried with many options but still same issue
return this.projectItemList.map(function(x) {
return (x.id);
}).indexOf(id);
How to overcome this performance issue?
If you can't change the structure of the array, you can run find method for the array.
var projectItemList = [{
"id":1,
"projectid":"a0I3l00001YiS67EAF",
"date":"2021-06-18",
"task":"",
"starttime":"",
"endtime":"",
"description":"",
"hours":"",
"projectname":"MyCOmp1",
"computedclass":"lookupclass1"
}, {
"id":2,
"projectid":"a0I3l00001YiS67EAF",
"date":"2021-06-18",
"task":"",
"starttime":"",
"endtime":"",
"description":"",
"hours":"",
"projectname":"Myproject",
"computedclass":"lookupclass1"
}];
const findIndex = id => projectItemList.find(i => i.id === id);
findIndex(1).description = 'bar';
console.log(projectItemList);
The complexity will be n, where n is the number of items in the array.
But the best solution, in my opinion, is changing your data to something like:
var list = {
1: {
name: 'name for 1',
description: 'description for 1',
},
2: {
name: 'name for 2',
description: 'description for 2',
}
};
from where, you can find the item like
list[1];
in 1 step.

Cartesian product (all combinations) in array of multi-dimentonal objects with flexible length

There are several questions with answers on StackOverflow which shows how to find Cartesian product for various simple arrays. And a wonderful article on RosettaCode. But I can't find any solution for my problem.
I have an array of object with items, let's call it items:
let items = [{
id: 1
quantity: 2
field: "other_field"
},
{
id: 2
quantity: 3
field: "other_field"
}]
Every item in this array have a pricing/crafting method and we could receive it by request.
let pricing = getPricing(id) //item id
/*
Which will return to us:
*/
pricing = [
{pricing_id: 1, reagent_items: [/*array of objects, fields exactly items*/]},
{pricing_id: 2, reagent_items: [/*array of objects, fields exactly items*/]}
]
CARTESIAN PRODUCT PROBLEM:
As you may already understand, according to the answer's title, I want to receive all possible combinations of items AND reagent_items from pricing methods.
For example, if we have two items and all every item (of this 2) have just one pricing method, there will be 4 different combinations:
2 default items from items
first default item from items (item[0]) and all all reagent_items from getPricing for item[1]
second default item from items (item[1]) and all all reagent_items from getPricing for item[0]
both reagent_items from getPricing for both default items
I literally can't push reagent items to items (or remove item from items) array, because items can be the same (include each other) Instead of it, I am using my own Array.prototype.method for adding/removal items from items array. It does just the same as push/slice but in more elegant way, manipulating with id and quantity fields.
The actual problem lies in the field of arrays.length and for ... loop.
When we evaluate default Cartesian product we know before the array.length and it's elements.
But in my case I should getPricing every items, then receive array of methods..
Schema:
It's like:
Default: I_1 I_2 ... N
/ \ / \ / \
Get Pricing: [I_A, I_B][I_B, I_C] [IN_J, IN_K],
[IN_Y, IN_X, IN_Z],
So it's not about finding: Cartesian([I_A, I_B],[I_B, I_C]) but something like:
I_1 + I_2
I_1 + (I_B, I_C)
(I_A, I_B) + I_2
(I_A, I_B) + (I_B, I_C)
...
So default item includes each others and their reagent_items and it's simple to find all combinations of two items, but when it's become 3+..
My current pseudo code for now:
/* inside async*/
...
let ArrayOfPricing = [] //length 2, where each Array represent Array of `pricing` for each item
Promise.all(items.map(async item => {
const itemPricing = await getPricing(item.id);
ArrayOfPricing.push(itemPricing );
})
/**And what's next? **/
for (let item of items) {
}
So I can't understand what should I do next, at this stage.
Should I loop/iterate every item? But if so, even if I iterate every item one-by-one and change/remove it and add it's reagent_items (for every pricing) I still don't change the next item/element in array of items and it's length more then just 2, then I won't receive all the combinations, it will be like:
for items
↓
item[1] → for pricing
→ for reagent_items
↓
replace item[1] for all reagent_item
item[2] /** they are still there, but I need iterate over it's pricing , too **/
item[3]
or I could calculate all possible combinations by looking for items length and all pricing length and then form and empty new array with fixed length and push to all the combinations. But if I iterate over it for push with for loop... I should combine items and it will be for loop, inside for loop, inside for .. loop..
So to be honest I am out of ideas. I don't ask to write full working code instead of me, but to point me the way out of this loop. How to get every combination for every item and "baby-items" inside of it? How many cycles should I use then? I'll be grateful for any useful idea/pseudocode/post link which will help me to deal with this case. I'm also here and will check all the comments and answers below.
UPD a simple version of «from what I get, to what I want»
from this:
[
{
field: "original, can be cloned for every combination",
items:
[
{id: 1, quantity: 2},
{id: 2, quantity: 3}
]
}
]
to:
[
{
field: "original",
items:
[
{id: 1, quantity: 2},
{id: 2, quantity: 3}
]
},
{
field: "combination1",
items:
[
{id: 11, quantity: 1}, //from getPricing(item[0])
{id: 12, quantity: 1}, //from getPricing(item[0])
{id: 2, quantity: 3}
]
},
{
field: "combination2",
items:
[
{id: 1, quantity: 2},
{id: 22, quantity: 3} //from getPricing(item[1])
{id: 23, quantity: 3} //from getPricing(item[1])
]
},
{
field: "combination3",
items:
[
{id: 11, quantity: 1}, //from getPricing(item[0])
{id: 12, quantity: 1}, //from getPricing(item[0])
{id: 22, quantity: 3} //from getPricing(item[1])
{id: 23, quantity: 3} //from getPricing(item[
]
}
//can be any length according to getPricing of every item, and I modify original array, but we could create a new one.
]
As I promised, I have found a solution of my problem and I'd like to share it with StackOverflow Community.
Pseudo-code:
let array = [
{
field: "original, can be cloned for every combination",
items:
[
{id: 1, quantity: 2},
{id: 2, quantity: 3}
]
}
]
for (let element of array) {
let MethodsCombinations = [];
for await (let forCombinations of element.items.map((item, i) => {
return getMethod(item.id) //get Method for each item)
})) {
MethodsCombinations.push(forCombinations)
}
/* Cartesian product */
let vanilla_CartesianProduct = MethodsCombinations.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
/* Return array of arrays, with objects inside like default array */
/**
* Other logic with two for loops and merging all the combinations and quantities
* with (my own) modified Array.prototype.addItemToArray
*/
}
I am very grateful to this Nina Scholz's answer and her awesome StackOverflow profile with all answers about combinations/permutations and for providing a support.

Javascript Map a Collection

The Issue:
I'm attempting to build a simple search tool. It returns a search query by matching an id to another item with the same id. Without going into the complexities, the issue I'm having is that when my data was organized previously, the map function from javascript returned all the results perfectly. However, now that my data is structured a bit differently (a collection, I think?) ....the ids don't appear to be lining up which causes the wrong search results to show.
The function in question:
const options = this.props.itemIds.map((id) => (
<Option key={this.props.itemSearchList[id].id}>
{this.props.itemSearchList[id].name}
</Option>
));
When the data was structured like this it worked as expected:
Example of previous structure:
const items = [
{
id: 0,
name: "name 0",
tags: ['#sports', '#outdoor', '#clothing'],
},
{
id: 1,
name: "name 1",
tags: ['#sports', '#outdoor', '#clothing'],
},
{
id: 2,
name: "Name 2",
tags: ['#sports', '#outdoor', '#clothing'],
},
Now that the data is a ?collection...the map function doesn't work as anticipated and it returns improper results or none at all: I've been able to use the lodash Map function on this structure successfully in the past.
Here's a screenshot of the new data:
I believe a representative way to write out the example would be:
const newItems = [
0: {
id: 0,
name: "name here",
},
1: {
id: 1,
name: "name here",
},
]
Any recommendations for making this work or need more info? Perhaps I'm misunderstanding the issue entirely, but I believe it has to do with data structure and the map function from JS. I can see results returning, but the id's are not lining up appropriately anymore.
Here's a visual representation of the misalignment. The orange is the search input and it pulling the right result. The green is the misalignment of what it's actually showing because of the data structure and mapping (I assume).
The issue is you were using index and lining that up with id as a sort of pseudo-key which is...beyond fragile. What you should be doing is keying by id (meaing itemsshould be an object) and then having a seperate array that stores the order you want. So items would be an object keyed by id:
const items = {
1: {
id: 1,
name: "name 1",
tags: ['#sports', '#outdoor', '#clothing'],
},
2: {
id: 2,
name: "name 2",
tags: ['#sports', '#outdoor', '#clothing'],
},
9: {
id: 9,
name: "Name 9",
tags: ['#sports', '#outdoor', '#clothing'],
},
};
And then itemIds (which it appears you already have) is an array with the correct order:
const itemIds = [1,9,2];
And then they can be accessed in the right order by looping over that array, and getting the element by said key:
itemIds.map((id) => {
const item = items[id];
// do something with the item
}
Take a look at how Redux recommends normalizing state shape.
https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape
What you call "collections" and "maps" are actually arrays. Now one of the arrays has the objects exactly at the position in the array that matches the id:
items[5].id === 5
Now through sorting /mutating / whatever you change the order so that the element at a certain position doesnt have that as an id:
newItems[5].id // 7 :(
That means that you cannot access the item that easy anymore, you now either have to sort the array again to bring it into order, or you search for an object with the id:
newItems.find(item => item.id === 5) // { id: 5, ... }
Or you switch over to some unsorted collections like a real Map:
const itemsMap = new Map(newItems.map(item => ([item.id, item])));
So you can get a certain item with its id as:
itemsMap.get(5) // { id: 5, ... }
... but the whole thing doesnt have to do with Array.prototype.map at all.
Here was my simple solution:
const options = [];
this.props.itemList.forEach((item) => {
if (this.props.searchResults.includes(item.id)) {
options.push(<Option key={item.id}>{item.name}</Option>);
}
});
Let me know what you think (to the group that tried to help!)

Find a Value in a Dictionary with a certain Property

Say I have a dictionary like
objDict = {
id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' },
}
If I wanted to search for the property "someId2" of the "id" property in Values of that dictionary.. how would I be able to grab the the whole object after finding it?
I really can't think of a good way to do it outside of iterating the dictionary with a for-in loop. I was thinking of using Object.values() but even then I can't think of a way of using that to grab the whole object that contains someId2.. I would only be able to determine if that property existed.
I was just wondering if there was a better way than a for-in loop. Thanks
you can just get all the keys using Object.keys(), then iterate over these and select the object you want based on the required id.
var objDict = {
id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' },
};
var obj;
Object.keys(objDict).forEach(x => obj = objDict[x].id === 'someId2' ? objDict[x]: obj);
console.log(obj);
There is Object.entries(), but for-in loop is more efficient than the other alternatives.
objDict = { id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' } }
item = Object.entries(objDict).find(a => a[1].id === 'someId2')
console.log(JSON.stringify(item))
The following code snippet containing break; statement could statistically provide a better performance vs. the selected one.
objDict = {
id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' },
};
var obj;
for (var key in objDict)
{
if (objDict[key].id==='someId2')
{
obj = objDict [key];
break;
}
}
console.log(obj);
ECMA Script 2015 provides another alternative of using Map object as described in : How to iterate (keys, values) in javascript?
Hope this may help.
This is a generic indexing problem, so no, there's no good way to do this other than looping through each value in the dictionary. This is because you've created a data structure to access the data quickly (O(1) time), but the drawback is that if you want to find the data through another index quickly, you'll have to search manually O(n) time for it.
If you really did care about the actual value of someId, then the canonical answer would be to create 2 separate dictionaries w/ the same data w/ different key values. This is very similar to having 2 indexes on a database table.

Categories

Resources