Ordering an array of object literals - javascript

I have this array of object.
I am trying to rearrange them by given order or sequence.
E.g if I say oil should come on order = 1 then the object of oil should come on top and if I say petrol should be order = 3 then the object of petrol should come on 3rd place.
Here is something I have tried so far but I am not achieving the correct result:
Also here is a working jsfiddle:
https://jsfiddle.net/voqgs7j8/
var utilities = {
"petrol": {
"title": "Petrol",
"price": 10
},
"oil": {
"title": "Oil",
"price": 12
},
"sugar": {
"title": "Sugar",
"price": 14
},
"flour": {
"title": "Flour",
"price": 65
} ,
"apple": {
"title": "apple",
"price": 20
} ,
"banana": {
"title": "banana",
"price": 30
} ,
"orange": {
"title": "orange",
"price": 19
}
};
//var getUtilityUserPreferences = {utilityOrders: []};// to test empty DB values
var getUtilityUserPreferences = {utilityOrders: [{"utility": "petrol", "order": 5}, {"utility": "oil", "order": 1}, {"utility": "flour", "order": 2}]};
var response = utilities;// by default it holds all same ordered values
if(getUtilityUserPreferences && getUtilityUserPreferences.utilityOrders && getUtilityUserPreferences.utilityOrders.length){
// OH we found user defined orders, so calculation starts with empty response
var response = {};
var resPointer = 1;// holds current index of final response, where the obj will be pushed
var dbArrIndex = 0;// current db arr index that is waiting for its turn, to be pushed
// loop through all values and keep pushing in final response unless some DB values comes and get pushed first
for(var key in utilities){
if(dbArrIndex < getUtilityUserPreferences.utilityOrders.length){// if db arr is not finished yet, otherwise we are done with db array
// trying to check if its time for DB values to be pushed, otherwise all of above loop will be pushed
var matching = true;
while(matching && dbArrIndex < getUtilityUserPreferences.utilityOrders.length){// loop can also finish db arr, so validating again
if(getUtilityUserPreferences.utilityOrders[dbArrIndex].order == resPointer){
response[getUtilityUserPreferences.utilityOrders[dbArrIndex].utility] = utilities[getUtilityUserPreferences.utilityOrders[dbArrIndex].utility];resPointer++;
delete utilities[getUtilityUserPreferences.utilityOrders[dbArrIndex].utility];
matching = true;
dbArrIndex++;
}
else{
matching = false;
}
}
}
if(utilities[key]){// could be null by upper if
response[key] = utilities[key];resPointer++;
}
}
}
//DONE!!!!!
for(var key in response){
//console.log(key);
}
console.log(response);
return response;

Related

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 Push to Array if Condition is Met

I have the following Foods Object:
var Foods = {
"Fruits": [{
"id": "1",
"Name": "Granny Smith",
"Category": "1"
}, {
"id": "2",
"Name": "Raspberries",
"Category": "1"
}
],
"Potatoes": [{
"id": "3",
"Name": "Maris Piper",
"Category": "2"
}, {
"id": "4",
"Name": "Charlotte",
"Category": "2"
}]
}
What I would like to do is only push the produce that matches an id passed by a link.
Get Foods
This is what I have tried so far:
function getCat (id){
result = [];
for(let item in Foods) {
if(Foods[item].id == id) {
data[item].foreach(v=>result.push("<div class='box'><h2>" +
data[key].Name + "<br></div>"));
}
}
}
display();
function display() {
alert(result);
}
So if a user hits the link (which has an id of 2), the result array should contain "Charlotte" and "Maris Piper" but I am just drawing a blank.
Any help appreciated.
Cheers
Youre quite close, however theres a slight problem:
for(let item in Foods) {
console.log(Foods[item]);
/*
[{
"id": "1",
"Name": "Granny Smith",
"Category": "1"
}, {
"id": "2",
"Name": "Raspberries",
"Category": "1"
}
]
*/
So youre iterating over the categories, which are arrays.
Foods[item].id
is undefined as its an array and not a product. So we need to iterate the array to, e.g.
var result=[];
Object.values(Foods).forEach(function(category){
category.forEach(function(product){
if(product.id===id){
result.push(product);
}
});
});
Run
But if youre doing this quite often, it might be easier to create one product array once:
var products = Object.values(Foods).reduce((arr,cat)=>arr.concat(cat),[]);
So you can simply filter this whenever someone clicks a button:
var result = products.filter(product=>product.id === id);
Run
You're somewhat on the right track, but what's data? Why are you not doing anything with result? And you should be looking at the Category property rather than ID.
This'll work:
function getCat(id) {
let result = [];
for (let item in Foods) {
if (Foods.hasOwnProperty(item)) {
Foods[item].forEach((food) => {
if (food.Category == id) {
result.push(food);
}
});
}
}
console.log(result);
}
First of all result array should be at global scope so that you can access it in another function, And in object you are having categories then each category has some data in array so after iterating over object, you need to iterate the items from array as well to get the value. Check the below code.
var result = [];
function getCat(id){
for(let item in Foods) {
var foodItem = Foods[item];
for(let i=0; i<foodItem.length; i++){
if(foodItem[i].id == id) {
result.push("<div class='box'><h2>" + foodItem[i].Name + "<br></div>"));
}
}
}
}
function display() {
alert(result);
}
display();
Iterator is wrong. You should do it like this:
function getCat(id){
result = [];
for(let item in Foods) {
Foods[item].forEach(function(each){
if(each.id == id) { // you cmpare to the wrong target
// do something
}
});
}
}

Looping between functions and storing results

I would like produce a list of grouped JSON elements according to a specific criteria, but I am unable to make my loop work.
The function should make groups of with 12 bottles and return a single JSON list. So in this example, the function should extract the 3 first items and then run again to extract the remaining ones. But I am looping forever... Thank you in advance,
var data = {
"order": [
{ "product": "MAXIMUS", "quantity": "3" },
{ "product": "COLECCION", "quantity": "3" },
{ "product": "CABERNET FRANC", "quantity": "6" },
{ "product": "CHARDONNAY", "quantity": "6" },
{ "product": "SAUVIGNON BLANC", "quantity": "6" }
]
};
var qtd = data.order;
var size = qtd.length;
var addline = '';
var add = '';
var total = 0;
var i = 0;
var a = 0;
var c = '';
function MakeList(i, add) {
for (i < 0; total < 12; i++) {
total += parseInt(qtd[i].quantity);
addline = addline + '{' + '"quantity": "' + qtd[i].quantity + ' units"},';
i = i++;
add = '{"Box of 12":[' + addline.slice(0, -1) + "]}";
}
return [i, add];
}
function BuildLabels(i, add) {
for (i < 0; c = "true"; i++) {
c = a[0] < size;
a += MakeList(i, add);
i = i++;
}
return a;
}
var results = BuildLabels(i, add);
output = { id: 3, results };
for (i < 0; c = "true"; i++)
something weird is happening here. You don't set any condition on cycle to stop, you just assign value "true" to c. Try to use == instead of =; also initialization looks strange - set i to 0. Apparently, It will make the whole thing work (at least the loop will stop at some point), but in the end I get that the variable results is equal to 0. There are other mistakes/weird stuff out there. Propably, you wanted to achieve something like this:
var data = {
"order": [
{ "product": "MAXIMUS", "quantity": "3" },
{ "product": "COLECCION", "quantity": "3" },
{ "product": "CABERNET FRANC", "quantity": "6" },
{ "product": "CHARDONNAY", "quantity": "6" },
{ "product": "SAUVIGNON BLANC", "quantity": "6" }
]
};
function MakeList(data) {
var selected = [], bottlesNum = 0;
for (var i = 0; bottlesNum < 12; i++) {
selected.push(data.order[i]);
bottlesNum += parseInt(data.order[i].quantity);
}
return selected;
}
var results = MakeList(data);
// now it is a JS object:
console.log({ id: 3, results: results });
// if you want it to be a JSON string, use JSON.stringify():
console.log(JSON.stringify({ id: 3, results: results }));
check it out.
UPDATE
var data = {
"order": [
{ "product": "MAXIMUS", "quantity": "3" },
{ "product": "COLECCION", "quantity": "3" },
{ "product": "CABERNET FRANC", "quantity": "6" },
{ "product": "CHARDONNAY", "quantity": "6" },
{ "product": "SAUVIGNON BLANC", "quantity": "6" }
]
};
function makeGroup(data, max) {
var selected = [], bottlesNum = 0;
while(data.order.length) {
if(bottlesNum + +data.order[0].quantity > max) break;
var order = data.order.shift();
bottlesNum += +order.quantity; // casting to Number
selected.push(order);
}
return selected;
}
function splitOrder(data, max) {
while(data.order.length) {
var results = makeGroup(data, max);
if(!results.length) {
console.log("Error: a product's quantity is greater than max. size of the group. Try to increase max. size of the group.");
break;
}
console.log({ results: results });
}
}
// 2nd argument - max. size of the group. In case of 12 there will be 2 groups - of 3, 3, 6 and 6, 6 bottles
splitOrder(data, 12);
// Also notice that if max. size of the group is a changing value and can be set somehow to, lets say, 4 which is fewer than number of some products (6) in our order. So, it is impossible to complete such a task without taking some additional steps to handle this situation. For example, we could filter our data beforehand to exclude products with numbars greater than 4 and then form groups based on the rest of the data. Or we can treat products with number equal to 6 as if they satisfy our constraint etc.

JavaScript/JSON - Get the total average of values having the same key from multiple arrays

I am receiving real-time responses from the back-end that contains the following JSON (almost every second):
One Array:
{
"newUpdate": [
{
"id": "TP",
"val" : 3
},
{
"id": "TPE20",
"val" : 3
}]
}
Another array (after one second or less)
{
"newUpdate": [
{
"id": "CRK",
"val" : 24
},
{
"id": "TPE20",
"val" : 44
}]
}
I am getting the above JSON almost every second knowing that each time it comes with different values and id's, and the array itself does not have a specific size.
Well, what I want to do is to get the average of the values having the same key 'id'.
For example, for the above array, the average will be for TPE20 :
(3+44)/2 =23.2 (as it computes the average for the id : TPE20)
Then it should show it here (using JQuery for example) [Think of the real-time average value like in the stock market]
<div id="TPE20"></div>
Currently, using the below for loop, I print the JSON listed above:
for(var i in load.updates){
var id = load.newUpdate[i].id;
updatesMap[id] = load.newUpdate[i].value;
var valueOfID = newUpdate[id];
}
The challenge is that I am receiving a lot of arrays at once (1/sec), each array contains different "id" and "val", I really don't know how I can compute the average using the way I described above!
Just use an object with keys representing the ids of the array objects and the values as objects containing the count, total, and average of those ids.
When you receive a new array simply update the object:
function updateObj(arr) {
arr.forEach(function(el) {
var key = el.id;
obj[key] = obj[key] || { count: 0, total: 0, avg: 0 };
obj[key].count++;
obj[key].total += el.val;
obj[key].avg = obj[key].total / obj[key].count;
});
}
Here's a simulation with setInterval sending one array to the function each second, and then displaying the completed object in the console.
Does this help? It get the Average of the search term like you asked. It uses jquery $.each to iterate through the array
var newdata = [
{
"newUpdate": [
{
"id": "TP",
"val" : 3
},
{
"id": "TPE20",
"val" : 3
}]
},
{
"newUpdate": [
{
"id": "CRK",
"val" : 24
},
{
"id": "TPE20",
"val" : 44
}]
}
]
function getAverage(array, term){
var sum = 0, n = 0;
$.each(array, function(i, item){
n++
var arrs = item.newUpdate
$.each(arrs, function(d, place){
// console.log(d)
if (place.id == term){
sum +=place.val
}
})
})
return sum / n
}
document.write(getAverage(newdata, "TPE20"))
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
You can use Array.prototype.forEach() , create a private property at an object to store number of occasions unique property occurs within callback; pass each object individually to function in succession to maintain averages of properties at single object
var a = {
"newUpdate": [{
"id": "TP",
"val": 3
}, {
"id": "TPE20",
"val": 3
}]
};
var b = {
"newUpdate": [{
"id": "CRK",
"val": 24
}, {
"id": "TPE20",
"val": 44
}]
}
var avg = {};
function update(update) {
update.newUpdate.forEach(function(value, index) {
if (!avg[value.id] || !avg.hasOwnProperty(value.id)) {
avg["_" + value.id] = 1;
avg[value.id] = value.val / avg["_" + value.id];
} else {
++avg["_" + value.id];
avg[value.id] += value.val;
avg[value.id] = avg[value.id] / avg["_" + value.id];
}
});
return avg
}
console.log(update(a), update(b), avg)

Create array from complex array objects & loops in javascript

I currently have a complex orders array (coming from a JSON client) that contains multiple orders like this (contains 2):
0: {
"employee": "Nicole"
"total": 13
"lineItems": {
"elements": [2]
0: {
"name": "Burger"
"price": 8
}
1: {
"name": "Lamb"
"price": 6.50
}
}
}
1: {
"employee": "Dan"
"total": 11
"lineItems": {
"elements": [2]
0: {
"name": "Lamb"
"price": 4.50
}
1: {
"name": "Meatballs"
"price": 6.50
}
}
}
What I want to do is create a new array that loops through the above and creates new items array based on the name of the lineItems object above. i.e. final output looks something like this:
var items = {
"Burger" = {
"totalSpent" : 8
},
"Lamb" = {
"totalSpent" : 13
// Note this totalSpent is an iteration or sum of all "price" items where name/id = "Lamb"
},
"Meatballs" = {
"totalSpent" : 4.50
}
}
I'm more used to PHP and have tried a number of different versions of this but can't seem to get the desired output. Here's what I've got so far:
var orders = //As above//
// Initialising new array to hold my final values
var orderItems = [];
for (var i = 0, len = orders.length; i < len; i++){
for(var e = 0, leng = orders[i]['lineItems']['elements'].length; e < leng; e++){
var totalSpent = 0;
var id = orders[i]['lineItems']['elements'][e]['name'];
if (orders[id] in orderItems[id]){
// overwrite existing array item
orderItems[id]['totalSpent'] += orders[i]['lineItems']['elements'][e]['price'];
orderItems[id].push({totalSpent : orderItems[id]['totalSpent']});
}
else {
// Create new array item
orderItems.push(id);
orderItems[id].push({totalSpent : orders[i]['lineItems']['elements'][e]['price']});
}
}
}
Edit:
Had to correct your orders syntax, I added it to my answer so you can run the Javascript Snippet;
Changed the whole dot notation to bracket notation to make it easier to read and understand;
Corrected the bug about items remaining an empty array (it was in the inner for);
var orders = [
{
"employee": "Nicole",
"total": 13,
"lineItems": {
"elements": [
{
"name": "Burger",
"price": 8
},
{
"name": "Lamb",
"price": 6.50
}
]
}
},
{
"employee": "Dan",
"total": 11,
"lineItems": {
"elements": [
{
"name": "Lamb",
"price": 4.50
},
{
"name": "Meatballs",
"price": 6.50
}
]
}
}
];
var items = {};
// loop in orders array
for (var i = 0; i < orders.length; i++) {
var elements = orders[i]["lineItems"]["elements"];
// loop in orders[i]["lineItems"]["elements"] object
for (var eIndex in orders[i]["lineItems"]["elements"]) {
// Add new item if it doesn't already exist
if (!items.hasOwnProperty(elements[eIndex]["name"])) {
items[elements[eIndex]["name"]] = {"totalSpent": elements[eIndex]["price"]};
} else {
// If it exists, sum totalSpent
items[elements[eIndex]["name"]]["totalSpent"] += elements[eIndex]["price"];
}
}
}
console.log(items);
PS: To find out why I'm using bracket notation instead of dot notation, check this question, it's good to know!
First of all, there are some error in your order array, note the difference between {} (for objects) and []. Then it is just simple use of the map function to iterate over the arrays.
See your browser console (F12) for the result of this snippet
var orders = [{
"employee": "Nicole",
"total": 13,
"lineItems": {
"elements": [{
"name": "Burger",
"price": 8
}, {
"name": "Lamb",
"price": 6.50
}
]
}
}, {
"employee": "Dan",
"total": 11,
"lineItems": {
"elements": [{
"name": "Lamb",
"price": 6.50
}, {
"name": "Meatballs",
"price": 4.50
}]
}
}]
var items = {}
orders.map(function(order) {
order.lineItems.elements.map(function(elem) {
if (items[elem.name]) {
items[elem.name].totalSpent += elem.price
} else {
items[elem.name] = {"totalSpent": elem.price}
}
})
})
console.log(items)

Categories

Resources