How do I get the combined length of multiple children in a JSON array? - javascript

I have the below dictionary in which I want to get the length all notifications (4).
[
{
"name": "3 Bedroom Fixer Upper",
"city": "Santa Rosa",
"id": 1,
"state": "CA",
"date_added": "2/3/14",
"completion_status": "20",
"notifications": [
"Quarterly Checklist",
"Pre-Summer",
"Annual Checklist"
],
"sq_ft": 2200,
"year_built": 1994,
"bedrooms": 3,
"bathrooms": 2.5,
"lot_size": 2.5
},
{
"name": "Ski Cabin",
"city": "Tahoe",
"id": 2,
"state": "CA",
"date_added": "3/3/14",
"completion_status": "45",
"notifications": [
"Quarterly Checklist"
],
"sq_ft": 1950,
"year_built": 1984,
"bedrooms": 3.5,
"bathrooms": 2,
"lot_size": 3
}
];
I am able to get the length of a single objects notifications (example all_properties[0].notifications.length = 3) but all_properties.notifications.length does not return anything. What would the syntax be to return the length of all notifications? (4)
http://jsfiddle.net/dakra/U3pVM/
Sorry if I used the incorrect lingo regarding JSON dictionaries (I am new to them.)

You can use some functional programming with Array.prototype.reduce:
function findSum(arr) {
return arr.reduce(function (a, b) {
return a + b.notifications.length;
}, 0);
}
and call it like findSum(all_properties).
For multiple arrays in array called arrays:
var totalSum = 0;
for (var i = 0; i < arrays.length; i += 1) {
totalSum = findSum(arrays[i]);
}

you can use the Array's reduce function
var data = [...your data array...]
var num = data.reduce(function(a,b){
return a+b.notifications.length;
},0);
console.log("Number of notifications:",num);

Related

Is there a ES6 shortcut way to compare array of elements and object key and remove the non-matching elements?

I am trying to find a shortcut or single line of code that can active following:
I have a array selectedFields consisting of elements:
selectedFields = [ "time", "age", "city" ]
I have a object valueProvided consisting of values:
valueProvided = { "time": "2022-10-01", "visa": "onArrival", "age": 30, "city": "paris", "hotel": "Holiday" }
I would like to compare the 2 fields and remove any elements from valueProvided object that are not available in the array. Meaning in this case the elements visa and hotel are not present within selectedFields so I would like to remove it from valueProvided so my final result would look something like this:
result = { "time": "2022-10-01", "age": 30, "city": "paris" }
I am aware that I can loop over the elements in an array and remove the non-matching key from the object to achieve this, but I am trying to find if there is any shortcut or ES6 way of doing it in a single line or efficiently.
I found a couple of answers but in all cases, they are trying to compare the object with another object or array with another array but not the cross approach to compare an array with the object or vice versa.
You can use Object.entries to get the entries from valueProvided, filter them on whether the key is in selectedFields, and then use Object.fromEntries to build the result object:
const selectedFields = ["time", "age", "city"]
const valueProvided = {
"time": "2022-10-01",
"visa": "onArrival",
"age": 30,
"city": "paris",
"hotel": "Holiday"
}
const result = Object.fromEntries(
Object.entries(valueProvided).filter(([k, v]) => selectedFields.includes(k))
)
console.log(result)
As pointed out by #NickParsons in the comments, you can further optimise this (as long as all the keys in selectedFields exist in valueProvided) by creating an object from mapping the keys in selectedFields to the key, value pair from valueProvided:
const selectedFields = ["time", "age", "city"]
const valueProvided = {
"time": "2022-10-01",
"visa": "onArrival",
"age": 30,
"city": "paris",
"hotel": "Holiday"
}
const result = Object.fromEntries(
selectedFields.map(k => [k, valueProvided[k]])
)
console.log(result)
If valueProvided was an array, you'd need to iterate that using map:
const selectedFields = ["time", "age", "city"]
const valueProvided = [{
"time": "2022-10-01",
"visa": "onArrival",
"age": 30,
"city": "paris",
"hotel": "Holiday"
},
{
"time": "2022-11-12",
"visa": "preBooked",
"age": 45,
"city": "london",
"hotel": "Business"
}
]
const result = valueProvided.map(v => Object.fromEntries(
Object.entries(v).filter(([k, v]) => selectedFields.includes(k))
// or
// selectedFields.map(k => [k, v[k]])
))
console.log(result)
This is a way to achieve it, I'm not sure whether this qualifies as a shortcut. Let me know:
selectedFields = [ "time", "age", "city" ]
valueProvided = { "time": "2022-10-01", "visa": "onArrival", "age": 30, "city": "paris", "hotel": "Holiday" }
for (let missing of selectedFields.filter(item => (valueProvided[item] !== undefined))) {
delete valueProvided[missing];
}
console.log(valueProvided);
Yes, there is a loop involved in order to remove the elements, but the search for missing elements is being done via calling filter().
This should work, although it's defenitely not the shortest answer:
selectedFields = [ "time", "age", "city" ];
valueProvided = { "time": "2022-10-01", "visa": "onArrival", "age": 30, "city": "paris", "hotel": "Holiday" }
console.log(valueProvided)
for (const key of Object.keys(valueProvided)) {
if(! selectedFields.includes(key)){
delete valueProvided[key]
}
}
console.log(valueProvided)
Another "concealed loop" approach using Array.reduce:
valueProvided = {
"time": "2022-10-01",
"visa": "onArrival",
"age": 30,
"city": "paris",
"hotel": "Holiday"
};
selectedFields = ["time", "age", "city"];
function pickProps(fromObject, propNames) {
return propNames.reduce((acc, propName) => {
acc[propName] = fromObject[propName];
return acc
}, {})
}
console.log(pickProps(valueProvided, selectedFields));
This does not mutate the source object. But like others in case property values are not of primitive types, it would reference them.
Direct mutating / re-assigment without helper function would be:
var valueProvided = {
"time": "2022-10-01",
"visa": "onArrival",
"age": 30,
"city": "paris",
"hotel": "Holiday"
};
valueProvided = ["time", "age", "city"].reduce((acc, propName) => {
acc[propName] = valueProvided[propName];
return acc
}, {});
console.log(valueProvided);
"Length golfing" comparison with the fromEntries approach:
result = selectedFields.reduce((a,k)=>(a[k]=valueProvided[k],a),{})
result = Object.fromEntries(selectedFields.map(k=>[k,valueProvided[k]]))
It also happens to be a bit faster for this use-case (bench), most probably because it does not create intermediate entries array "map" but starts from "empty" result object and adds properties directly to it during reduce instead.

Nested array issue in JavaScript

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

JavaScript: Creating 2 dimensional array of objects based on condition

I'm working on a project that involves two dimensional arrays of objects. I've been working to try to figure out this answer for a while now, and I have some ideas of how to solve it, but I'm a bit stumped.
Let's assume there are animal shelters in City A and that each can hold 50 animals. Animals are coming in from different parts of the state, but the amount of animals from one place can never be more than 50. Here's an example of the animals coming in.
let animal_shelter_capacity =< 50;
let array 1 = [
{ "region": "NE", quantity: 25 },
{ "region": "NW", quantity: 21 },
{ "region": "SE", quantity: 43 },
{ "region": "SW", quantity: 18 },
{ "region": "Central", quantity: 20}
]
In this example, the animals from NE (25) and NW (21) would go to one shelter (46 animals in total), the animals from SE (43) would go to another shelter (43 animals in total), and the animals from SW (18) and Central (20) would go to a third shelter (38 animals in total). The number of animals in one shelter can never be greater than 50.
So, I need to produce an array that looks like this:
let array2 = [
[ { "region": "NE", quantity: 25 }, { "region": "NW", quantity: 21 }],
[ { "region": "SE", quantity: 43 } ],
[ { "region": "SW", quantity: 18 }, { "region": "Central", quantity: 20} ]
]
I'm able to loop through array1 using forEach, but when it comes to adding until a certain value is reached, then creating a new array of arrays, I'm a little stumped on how to proceed to do this.
Here's what I have so far:
let array2 = [] //instantiate array
array1.forEach(function(element, index, array)
{
let sum = 0;
let capacity = 50;
for (let j = 0; j < array1.length; j++)
{
sum += array1[j].quantity;
if (sum >= capacity)
{
//create the new array consisting of the regions, push it into the larger array2
}
}
})
I'm not sure how to continue doing this. I know I need to do the following:
1. find a way to cut off the addition sequence once the quantity reaches 50
2. reset the sequence
3. form the new arrays and push them into a larger array
Can anyone provide any advice on how to proceed?
Try this. Loop through shelters, if it can fit, add it to the current shelter list. If not, save the current shelter roster and start a new one. After the loop, make sure to save the current roster being written
const locations = [{
"region": "NE",
"quantity": 25
},
{
"region": "NW",
"quantity": 21
},
{
"region": "SE",
"quantity": 43
},
{
"region": "SW",
"quantity": 18
},
{
"region": "Central",
"quantity": 20
}
]
const shelterRoster = [];
const capacity = 50;
let count = 0;
for (let location of locations) {
let shelter = shelterRoster[shelterRoster.length - 1];
if (!shelter || count + location.quantity > capacity) {
shelterRoster.push([location]);
count = 0;
} else {
shelter.push(location);
}
count += location.quantity
}
console.log(shelterRoster);
You can approach this with reduce(), using a custom object as the initial accumulator. When the reduce finish, you will need to do an extra line of code for get your final result.
const animal_shelter_capacity = 50;
const array1 = [
{"region": "NE", quantity: 25},
{"region": "NW", quantity: 21},
{"region": "SE", quantity: 43},
{"region": "SW", quantity: 18},
{"region": "Central", quantity: 20}
];
let obj = array1.reduce((res, curr) =>
{
let test = (curr.quantity + res.c >= animal_shelter_capacity);
return {
r: test ? [...res.r, res.a] : res.r,
c: test ? curr.quantity : res.c + curr.quantity,
a: test ? [curr] : [...res.a, curr]
};
},{r:[], c:0, a:[]});
let newArr = [...obj.r, obj.a];
console.log(newArr);
On the previous code, the accumulated object have the next keys:
r: The array of shelters generated progressively.
c: The counter of animals of the current shelter (see next).
a: The current shelter of animals.
When the reduce finish, the last shelter (that on the property a) will not be on the array of shelters. So, we have to put it manually (this is what the extra line does).
The first point I've come up with, is you need to sort input data first. because your given input ( as asked in the question ) is not the only possible way of having data.
You can have some data like:
let array1 = [
{ "region": "NE", quantity: 25 },
{ "region": "NW", quantity: 21 },
{ "region": "Central", quantity: 20 },
{ "region": "SE", quantity: 43 },
{ "region": "SW", quantity: 18 },
]
and in this example, we should have pushed central and SW together, but not sorting the input at first place will result central and SW in different arrays.
So, conclusion. I think this is gonna work:
var make = function( arr ) {
var res = [],
currentArr = [];
arr.forEach( v => {
sum += v.quantity;
if ( sum <= capacity ) {
currentArr.push( v );
} else {
res.push( currentArr );
currentArr = [ v ];
sum = v.quantity;
}
});
res.push( currentArr );
return res;
},
array1 = [
{ "region": "NE", quantity: 25 },
{ "region": "NW", quantity: 21 },
{ "region": "Central", quantity: 20 },
{ "region": "SE", quantity: 43 },
{ "region": "SW", quantity: 18 }
],
sum = 0,
result,
capacity = 50;
array1.sort( ( a, b ) => {
return a.quantity - b.quantity;
});
console.log( array1 );
result = make( array1 );
console.log( result );

Count object property inside array using lodash or vanilla javascript

I have this object with nested arrays/objects:
{
"USA": [
{
"location": "New York",
"municipality": "Manhattan",
},
{
"location": "Texas",
"municipality": "Austin",
}
],
"CANADA": [
{
"location": "Ontario",
"municipality": "no municipality",
}
]
}
I want to use lodash or plain javascript to count how many location are inside the USA and CANADA. How is that possible?
desired result:
USA: 2
CANADA: 1
Just use the array lengths:
var USA = myObj.USA.length;
var Canada = myObj.CANADA.length;
Or, for larger data sets:
var result = {};
Object.keys(myObj)
.forEach(function(key,index) {
result[key] = myObj[key].length;
});
With lodash you could use mapValues:
let result = _.mapValues(data, 'length');
The solution using Array.prototype.reduce() function:
var obj = {
"USA": [ { "location": "New York", "municipality": "Manhattan" }, { "location": "Texas", "municipality": "Austin" } ], "CANADA": [ { "location": "Ontario", "municipality": "no municipality" }]
},
result = Object.keys(obj).reduce(function(r,k){
r[k] = obj[k].length;
return r;
}, {});
console.log(result)

reset object order javascript

I have a object like this
{
"items":{
"2":{
"id":122,
"product_id":"DE",
"price":"9.35",
},
"4":{
"id":15,
"product_id":"CH",
"price":"8.00",
}
"7":{
"id":78,
"product_id":"CH",
"price":"3.00",
}
},
"total_price":"20.35",
"item_count":2,
"unit":"CHF"
}
Do you know how i reset the items order.
now 2, 4, 7
should be 0, 1, 2
Created a JSfiddle that shows you a way.
Im using a custom format function:
function format(object) {
var items = {};
var i = 0;
for (var index in object.items) {
items[i] = object.items[index];
i++;
}
object.items = items;
}
The resulted object is this:
{
"items": {
"0": {
"id": 122,
"product_id": "DE",
"price": "9.35"
},
"1": {
"id": 15,
"product_id": "CH",
"price": "8.00"
},
"2": {
"id": 78,
"product_id": "CH",
"price": "3.00"
}
},
"total_price": "20.35",
"item_count": 2,
"unit": "CHF"
}
How about this
var obj = {
"items":{
"2":{
"id":122,
"product_id":"DE",
"price":"9.35",
},
"4":{
"id":15,
"product_id":"CH",
"price":"8.00",
},
"7":{
"id":78,
"product_id":"CH",
"price":"3.00",
}
},
"total_price":"20.35",
"item_count":2,
"unit":"CHF"
}
var keys = Object.keys(obj.items)
for (var i = 0; i < keys.length; i++) {
obj.items[i] = obj.items[keys[i]];
delete obj.items[keys[i]];
};
console.log(obj);
Object properties do not have order. I assume you want to re-name the properties, counting up from 0, but have the properties maintain the original relative ordering of their keys. (So the property with the smallest name is renamed to 0, the second-to-smallest is 1, etc.)
To do this, get all the property names, and sort the names numerically. Then, get all the values in the same over as their sorted property names. Finally, re-insert those property values with their new property names.
var itemsObj = obj["items"];
// get all names
var propertyNames = Object.keys(itemsObj);
// sort property names in numeric order: ["2", "4", "7"]
propertyNames.sort(function(a,b){ return a-b; });
// get property values, sorted by their property names
// ["2", "4", "7"] becomes [{ "id":122, .. }, { "id":15, ... }, { "id":78, ... }]
var values = propertyNames.map(function(propName) { return itemsObj[propName]; }
// clear out old property and add new property
for(var i=0; i<values.length; ++i) {
delete itemsObj[propertyNames[i]];
itemsObj[i] = values[i];
}
var data = {
"items": {
"2": {
"id": 122,
"product_id": "DE",
"price": "9.35",
},
"4": {
"id": 15,
"product_id": "CH",
"price": "8.00",
},
"7": {
"id": 78,
"product_id": "CH",
"price": "3.00",
}
},
"total_price": "20.35",
"item_count": 2,
"unit": "CHF"
};
var indices = Object.keys(data.items).map(function(i) { return parseInt(i, 10); }),
counter = 0;
indices.sort();
indices.forEach(function (i) {
if (i > counter) { // put here some more collision detecting!
data.items[counter] = data.items[i];
delete data.items[i];
counter++;
}
});
Object properties order is not guaranteed anyway. You should use an array instead.
Take a look at this answer

Categories

Resources