Related
I have the following array
Array["MyArray",
{
"isLoaded":true,
"items":
[{
"id":"4",
"name":"ProductA",
"manufacturer":"BrandA",
"quantity":1,
"price":"25"
},{
"id":"1",
"name":"ProductB",
"manufacturer":"BrandB",
"quantity":5,
"price":"20"
}],
"coupons":null
}
]
I need to load product names and their quantity from the array.
const result = [key, value].map((item) => `${item.name} x ${item.quantity}`);
Here's one possible way to achieve the desired result:
const getProductsAndQuantity = ([k , v] = arr) => (
v.items.map(it => `${it.name} x ${it.quantity}`)
);
How to use it within the context of the question?
localforage.iterate(function(value, key, iterationNumber) {
console.log([key, value]);
const val2 = JSON.parse(value);
if (val2 && val2.items && val2.items.length > 0) {
console.log(val2.items.map(it => `${it.name} x ${it.quantity}`).join(', '))
};
});
How it works?
Among the parameters listed in the question ie, value, key, iterationNumber, only value is required.
The above method accepts the key-value pair as an array (of 2 elements) closely matching the console.log([key, value]); in the question
It uses only v (which is an object). On v, it accesses the prop named items and this items is an Array.
Next, .map is used to iterate through the Array and return each product's name and quantity in the desired/expected format.
Test it out on code-snippet:
const arr = [
"MyArray",
{
"isLoaded": true,
"items": [{
"id": "4",
"name": "ProductA",
"manufacturer": "BrandA",
"quantity": 1,
"price": "25"
}, {
"id": "1",
"name": "ProductB",
"manufacturer": "BrandB",
"quantity": 5,
"price": "20"
}],
"coupons": null
}
];
const getProductsAndQuantity = ([k, v] = arr) => (
v.items.map(
it => `${it.name} x ${it.quantity}`
)
);
console.log(getProductsAndQuantity());
I understood. You should learn about array methods such as map, filter, reduce. Here you go...
const items = [{
"id":"4",
"name":"ProductA",
"manufacturer":"BrandA",
"quantity":1,
"price":"25"
},{
"id":"1",
"name":"ProductB",
"manufacturer":"BrandB",
"quantity":5,
"price":"20"
}];
const result = items.map((item) => `${item.name} x ${item.quantity}`);
console.log(result);
I think I understand the question to say that the input is an array of objects, each containing an array of items. The key is that a nested array requires a nested loop. So, we iterate the objects and their internal items (see the lines commented //outer loop and // inner loop below)
Also, half-guessing from the context, it looks like the that the OP aims to assemble a sort of invoice for each object. First a demo of that, (and see below for the version simplified to exactly what the OP asks)...
const addInvoice = obj => {
let total = 0;
// inner loop
obj.invoice = obj.items.map(i => {
let subtotal = i.quantity * i.price;
total += subtotal
return `name: ${i.name}, qty: ${i.quantity}, unit price: ${i.price}, subtotal: ${subtotal}`
});
obj.invoice.push(`invoice total: ${total}`);
}
const objects = [{
"isLoaded": true,
"items": [{
"id": "4",
"name": "ProductA",
"manufacturer": "BrandA",
"quantity": 1,
"price": "25"
}, {
"id": "1",
"name": "ProductB",
"manufacturer": "BrandB",
"quantity": 5,
"price": "20"
}],
"coupons": null
}]
// outer loop
objects.forEach(addInvoice);
console.log(objects);
If my guess about the goal went to far, just remove the unit price, subtotal and total lines from the invoice function...
const objects = [{
"isLoaded": true,
"items": [{
"id": "4",
"name": "ProductA",
"manufacturer": "BrandA",
"quantity": 1,
"price": "25"
}, {
"id": "1",
"name": "ProductB",
"manufacturer": "BrandB",
"quantity": 5,
"price": "20"
}],
"coupons": null
}]
const summaryString = obj => {
return obj.items.map(i => `${i.name}, ${i.quantity}`);
}
const strings = objects.map(summaryString);
console.log(strings);
I have a problem. I have an object with this structure like this example.
{
"Name": "Peter",
"Username": "dummy",
"Age": 18,
"moreData": {
"tags": [1,2,3],
"hasCar": true,
"preferences": {
"colors": ["green", "blue"]
}
}
}
I would like to convert it to an array like the following. I am desperate and can not get any further. I have issues as soon I get some nested objects. Does someone have an idea how I can achieve this? Kind Regards
[
{
"key": "Name",
"val": "Peter"
},
{
"key": "Username",
"val": "dummy"
},
{
"key": "Age",
"val": "18"
},
{
"key": "tags",
"val": [1,2,3]
},
{
"key": "hasCar",
"val": true
},
{
"key": "colors",
"val": ["green", "blue"]
}
]
For this you need to first iterate through all the key value pairs of your object and change the specific type of data into name value pairs except the nested objects. If the value in the object at a certain key is an object then the same procedure has to be done for it. And since there can be N number of levels for this nested data thus we need a recursive function for it. Whenever we have to do a same set of processing for nested data then it always means it can be done using recursion. It can be done via for loops too but a recursive function is much clear and lesser to write.
function getData(data) {
let results = [];
Object.keys(data).forEach(key => {
// If the type of the data item is object and is not an array, go into recursion
if(typeof data[key] == 'object' && !Array.isArray(data[key])) {
results = results.concat(getData(data[key]));
} else {
results.push({ key, val: data[key] });
}
});
return results;
}
const data = {
"Name": "Peter",
"Username": "dummy",
"Age": 18,
"moreData": {
"tags": [1,2,3],
"hasCar": true,
"preferences": {
"colors": ["green", "blue"]
}
}
};
const results = getData(data);
console.log(results);
// [{"key":"Name","val":"Peter"},{"key":"Username","val":"dummy"},{"key":"Age","val":18},{"key":"tags","val":[1,2,3]},{"key":"hasCar","val":true},{"key":"colors","val":["green","blue"]}]
I have two arrays:
const array1 = [{
"id": "4521",
"name": "Tiruchirapalli",
"stateId": "101"
},
{
"id": "1850",
"name": "Tenkasi",
"stateId": "101"
},
{
"id": "202",
"name": "Thanjavur",
"stateId": "101"
},
{
"id": "505",
"name": "Ernakulam",
"stateId": "102"
},
];
And now array2
const array2 = [{
"id": 1850,
"cityName": "Tenkasi",
"aliasNames": [
"Thenkasi"
]
},
{
"id": 4521,
"cityName": "Tiruchirapalli",
"aliasNames": [
"Trichy"
]
},
{
"id": 202,
"cityName": "Thanjavur",
"aliasNames": [
"Tanjore"
]
},
{
"id": 505,
"cityName": "Ernakulam",
"aliasNames": [
"Kochi",
"Cochin"
]
},
];
what i need to do is, how to filter both the arrays at same time ( or filter first one and then second which ever one is performance effective ).
For instance, when user types "Kochi", first it should check on array1 to find if its has name="Kochi", if it has then we can set the state with that and if it doesnt have we need to find it on array2 and the update the state !
Which is fast and effective way to handle this - ( array1 has 2500 records and array2 has 990 records ) so performance / speed is also a concern
My attempt:
searchFilterFunction = text => {
this.setState({ typedText: text });
const newData = array1.filter(item => {
const itemData = `${item.name.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({ data: newData});
};
How to implement the second filter in optimized way ?
For instance, when user types "Kochi", first it should check on array1
to find if its has name="Kochi", if it has then we can set the state
with that and if it doesnt have we need to find it on array2 and the
update the state !
I would do something like this with Array.find.
if( array1.find(item=>item.name.toUpperCase() === text) ) {
// set state
} else if( array2.find(item=>item.cityName.toUpperCase() === text) ) {
// set state
}
A refined form would be
let result = array1.find(item=>item.name.toUpperCase() === text);
// check in array 2 as we cannot find in array 1
if(!result) {
result = array2.find(item=>{
// check in aliasNames and in cityName
return item.cityName.toUpperCase() === text || item.aliasNames.includes(text);
}
);
}
if(result) {
setState(result);
} else {
// place not found
}
Regarding the performance based on your array count you will not see much difference. If you want to save some milliseconds you can check the array with least count first as mentioned in one of the comments. But the time also varies based on were the element is in array.
I think this is the most optimal solution because nesting the two filter won't work as you need to filter from first array and then second.
const array1 = [{
"id": "4521",
"name": "Tiruchirapalli",
"stateId": "101"
},
{
"id": "1850",
"name": "Tenkasi",
"stateId": "101"
},
{
"id": "202",
"name": "Thanjavur",
"stateId": "101"
},
{
"id": "505",
"name": "Ernakulam",
"stateId": "102"
},
];
const array2 = [{ "id": 1850, "cityName": "Tenkasi",
"aliasNames": [
"Thenkasi"
]
},{"id": 4521,"cityName": "Tiruchirapalli",
"aliasNames": [
"Trichy"
]
},
{
"id": 202,
"cityName": "Thanjavur",
"aliasNames": [
"Tanjore"
]
},
{
"id": 505,
"cityName": "Ernakulam",
"aliasNames": [
"Kochi",
"Cochin"
]
},
];
function filter(text) {
// Complexity Linear
const filter_array = array1.filter((a) => {
return (a.name === text)
});
if (filter_array.length > 0) {
//Set State and return
}
//Complexity Linear and includes complexity Linear O(sq(m*n)) where n is //the aliasName record
const filter_array2 = array2.filter((a) => {
return a.cityName === text || a.aliasNames.includes(text);
});
return filter_array2 //Set State filter array 2
}
console.log(filter("Kochi"));
How can we push values to an object from inside a map function and return that single object. I have string comparison condition inside the map function. I tried using Object.assign but it returns an array with multiple object inside that array. Instead of this multiple object I'm expecting a single object inside an array.
Map function
let arrayObj = arrayToTraverse.map(function(item) {
var myObj = {};
if(item.inputvalue === 'Name'){
Object.assign(myObj, {name: item.value});
} else if (item.inputvalue === 'Email'){
Object.assign(organizerInfo, {email: item.value});
} else if (item.inputvalue === 'Company'){
Object.assign(organizerInfo, {company: item.value});
}
return myObj;
});
console.log("The array object is", arrayObj)
This return the array of objects as follows
[
{
"name": "Tom"
},
{
"email": "tom#abc.com"
},
{
"company": "ABC"
}
]
But The array I'm expecting is
[
{
"name": "Tom",
"email": "tom#abc.com",
"company": "ABC"
}
]
// or
[
"returned": {
"name": "Tom",
"email": "tom#abc.com",
"company": "ABC"
}
]
An example of arrayToTraverse can be considered as following
[
{
"id": "1",
"inputvalue": "Name",
"value": "Tom",
"type": "Short Text"
},
{
"id": "2",
"inputvalue": "Email",
"value": "tom#abc.com",
"type": "Email ID"
},
{
"id": "3",
"inputvalue": "Company",
"value": "Google",
"type": "Long Text"
}
]
Simply put, you're trying to reduce an array to a single object, not map one array to another.
var arrayToTraverse = [
{inputvalue:"Name",value:"Tom"},
{inputvalue:"Email",value:"tom#abc.com"},
{inputvalue:"Company",value:"ABC"},
{inputvalue:"Foo",value:"Bar"} // wont show up
];
var valuesRequired = ["Name","Email","Company"];
var result = arrayToTraverse.reduce( (acc, item) => {
if(valuesRequired.includes(item.inputvalue))
acc[item.inputvalue.toLowerCase()] = item.value;
return acc;
}, {});
console.log(result);
Edit: Added lookup array for required fields.
This Object have relationship as: childOne > childTwo > childThree > childFour > childFive > childSix.
{
"parentObj": {
"childOne": [
{
"name": "A",
"id": "1"
},
{
"name": "B",
"id": "2"
}
],
"childTwo": [
{
"name": "AB",
"parent_id": "1",
"id": "11"
},
{
"name": "DE",
"parent_id": "2",
"id": "22"
}
],
"childThree": [
{
"name": "ABC",
"parent_id": "22",
"id": "111"
},
{
"name": "DEF",
"parent_id": "11",
"id": "222"
}
],
"childFour": [
{
"name": "ABCD",
"parent_id": "111",
"id": "1111"
},
{
"name": "PQRS",
"parent_id": "111",
"id": "2222"
}
],
"childFive": [
{
"name": "FGRGF",
"parent_id": "1111",
"id": "11111"
},
{
"name": "ASLNJ",
"parent_id": "1111",
"id": "22222"
},
{
"name": "ASKJA",
"parent_id": "1111",
"id": "33333"
}
],
"childSix": [
{
"name": "SDKJBS",
"parent_id": "11111",
"id": "111111"
},
{
"name": "ASKLJB",
"parent_id": "11111",
"id": "222222"
}
]
}
}
Is there any way to delete an item by ID and the objects which are associated with that particular ID should get deleted(i.e., If I do delete parentObj.childTwo[1], then all the related object beneath it should also gets deleted).
Looping manually is too bad code, and generate bugs. There must be better ways of dealing with this kind of problems like recursion, or other.
The data structure does not allow for efficient manipulation:
By nature objects have an non-ordered set of properties, so there is no guarantee that iterating the properties of parentObj will give you the order childOne, childTwo, childThree, ... In practice this order is determined by the order in which these properties were created, but there is no documented guarantee for that. So one might find children before parents and vice versa.
Although the id values within one such child array are supposed to be unique, this object structure does not guarantee that. Moreover, given a certain id value, it is not possible to find the corresponding object in constant time.
Given this structure, it seems best to first add a hash to solve the above mentioned disadvantages. An object for knowing a node's group (by id) and an object to know which is the next level's group name, can help out for that.
The above two tasks can be executed in O(n) time, where n is the number of nodes.
Here is the ES5-compatible code (since you mentioned in comments not to have ES6 support). It provides one example call where node with id "1111" is removed from your example data, and prints the resulting object.
function removeSubTree(data, id) {
var groupOf = {}, groupAfter = {}, group, parents, keep = { false: [], true: [] };
// Provide link to group per node ID
for (group in data) {
data[group].forEach(function (node) {
groupOf[node.id] = group;
});
}
// Create ordered sequence of groups, since object properties are not ordered
for (group in data) {
if (!data[group].length || !data[group][0].parent_id) continue;
groupAfter[groupOf[data[group][0].parent_id]] = group;
}
// Check if given id exists:
group = groupOf[id];
if (!group) return; // Nothing to do
// Maintain list of nodes to keep and not to keep within the group
data[group].forEach(function (node) {
keep[node.id !== id].push(node);
});
while (keep.false.length) { // While there is something to delete
data[group] = keep.true; // Delete the nodes from the group
if (!keep.true.length) delete data[group]; // Delete the group if empty
// Collect the ids of the removed nodes
parents = {};
keep.false.forEach(function (node) {
parents[node.id] = true;
});
group = groupAfter[group]; // Go to next group
if (!group) break; // No more groups
// Determine what to keep/remove in that group
keep = { false: [], true: [] };
data[group].forEach(function (node) {
keep[!parents[node.parent_id]].push(node);
});
}
}
var tree = {"parentObj": {"childOne": [{"name": "A","id": "1"},{"name": "B","id": "2"}],"childTwo": [{"name": "AB","parent_id": "1","id": "11"},{"name": "DE","parent_id": "2","id": "22"}],"childThree": [{"name": "ABC","parent_id": "22","id": "111"},{"name": "DEF","parent_id": "11","id": "222"}],"childFour": [{"name": "ABCD","parent_id": "111","id": "1111"},{"name": "PQRS","parent_id": "111","id": "2222"}],"childFive": [{"name": "FGRGF","parent_id": "1111","id": "11111"},{"name": "ASLNJ","parent_id": "1111","id": "22222"},{"name": "ASKJA","parent_id": "1111","id": "33333"}],"childSix": [{"name": "SDKJBS","parent_id": "11111","id": "111111"},{"name": "ASKLJB","parent_id": "11111","id": "222222"}]}}
removeSubTree(tree.parentObj, "1111");
console.log(tree.parentObj);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Sure, the function you use to delete an entry should FIRST recurse, which means run itself on the linked entry, unless there is none. So, in psuedocode
function del(name, index)
{
if parent[name][index] has reference
Then del(reference name, reference ID)
Now del parent[name][index]
}
No loop needed.
And since we stop if there is no reference, we do not recurse forever.
Not sure what it is you want but maybe this will work:
const someObject = {
"parentObj": {
"childOne": [
{
"name": "A",
"id": "1"
},
{
"name": "B",
"id": "2"
}
],
"childTwo": [
{
"name": "AB",
"childOne": "1",
"id": "11"
},
{
"name": "DE",
"childOne": "2",
"id": "22"
}
]
}
};
const removeByID = (key,id,parent) =>
Object.keys(parent).reduce(
(o,k)=>{
o[k]=parent[k].filter(
item=>
!(Object.keys(item).includes(key)&&item[key]===id)
);
return o;
},
{}
);
const withoutID = Object.assign(
{},
someObject,
{ parentObj : removeByID("childOne","1",someObject.parentObj) }
);
console.log(`notice that childTwo item with childOne:"1" is gone`);
console.log("without key:",JSON.stringify(withoutID,undefined,2));
const otherExample = Object.assign(
{},
someObject,
{ parentObj : removeByID("childOne","2",someObject.parentObj) }
);
console.log(`notice that childTwo item with childOne:"2" is gone`);
console.log("without key:",JSON.stringify(otherExample,undefined,2));
const both = Object.assign(
{},
someObject,
{ parentObj : removeByID("childOne","1",otherExample.parentObj) }
);
console.log(`notice that childTwo items with childOne are both gone`);
console.log("without key:",JSON.stringify(both,undefined,2));