How can I access this property in my dictionary in js? - javascript

I thought I understood how to loop through a dictionary, but my loop is wrong. I try to access the name of each sub item but my code does not work.
Here is what I did:
list = [
{
title: 'Groceries',
items: [
{
id: 4,
title: 'Food',
cost: 540 ,
},
{
id: 5,
title: 'Hygiene',
cost: 235,
},
{
id: 6,
title: 'Other',
cost: 20,
},
],
}];
function calculateCost(){
let total = 0;
Object.keys(list).forEach((k) => { for (i in k.items) { total += i.data; } });
console.log(total);
return total;
}

Your list is an array includes 1 object and this object has two properties title and items the items here is an array of objects each one of these objects has property cost so to calculate the total cost you need to loop through items array, here is how you do it:
let list = [
{
title: 'Groceries',
items: [
{
id: 4,
title: 'Food',
cost: 540 ,
},
{
id: 5,
title: 'Hygiene',
cost: 235,
},
{
id: 6,
title: 'Other',
cost: 20,
},
],
}];
function calculateCost(){
let total = 0;
list[0].items.forEach(el => {
total += el.cost;
})
console.log(total)
return total;
}
calculateCost();

Your list is an Array, not an Object.
Instead of Object.keys() use Array.prototype.reduce:
const calculateCost = (arr) => arr.reduce((tot, ob) =>
ob.items.reduce((sum, item) => sum + item.cost, tot), 0);
const list = [
{
title: 'Groceries',
items: [
{id: 4, title: 'Food', cost: 10},
{id: 5, title: 'Hygiene', cost: 20},
{id: 6, title: 'Other', cost: 30}
]
}, {
title: 'Other',
items: [
{id: 8, title: 'Scuba gear', cost: 39}
],
}
];
console.log(calculateCost(list)); // 99

Expanding on #Roko's and #mmh4all's answers, the following code adds several verification statements to handle cases where a deeply nested property in your data is not what you expect it to be.
const calculateCost = (orders) => {
let listOfCosts = [];
// For each 'order' object in the 'orders' array,
// add the value of the 'cost' property of each item
// in the order to 'listOfCosts' array.
orders.forEach(order => {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
if (!Array.isArray(order.items)) { return; }
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat
const orderCostArr = order.items.map(item =>
isNaN(item.cost) ? 0 : parseFloat(item.cost, 10));
if (orderCostArr.length === 0) { return; }
// Concatenate 'orderCostArr' to the 'listOfCosts' array
//listOfCosts = listOfCosts.concat(orderCostArry);
// Alternate approach is to use the spread syntax (...) to
// push the items in the array returned by 'order.items.map()'
// into the 'listOfCosts' array.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
listOfCosts.push(...orderCostArr);
});
// Use the 'reduce' method on the 'listOfCosts' array
// to get the total cost.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
const totalCost = listOfCosts.reduce(
(accumulator, currentValue) => accumulator + currentValue, 0);
return totalCost;
};
const list = [
{
title: 'Groceries',
items: [
{ id: 4, title: 'Food', cost: 10 },
{ id: 3, title: 'Baked goods', cost: 20 },
{ id: 5, title: 'Hygiene', cost: 0 },
{ id: 6, title: 'Other' }
]
}, {
title: 'Gear',
items: {},
}, {
title: 'Accessories',
items: [],
}, {
title: 'Bags',
}, {
title: 'Other',
items: [
{ id: 10, title: 'Scuba gear', cost: "5" },
{ id: 8, title: 'Scuba gear', cost: "err" },
{ id: 9, title: 'Scuba gear', cost: 59 }
],
}
];
console.log(calculateCost(list)); // 94

Related

Filter array of objects dynamically according to another array of objects

So I am making a filter functionality for React, so I have an array of objects, and based on another array that contains values to filter the array, I need to get the filtered values.
code: the array of objects to apply the filter to:
const citiesData = [
{
id: 1,
name: 'amritsar',
popu: '1200'
},
{
id: 2,
name: 'jalandhar',
popu: '1300'
},
{
id: 3,
name: 'phagwara',
popu: '1200'
},
{
id: 4,
name: 'ludhiana',
popu: '1400'
},
{
id: 5,
name: 'mumbai',
popu: '2000'
},
{
id: 6,
name: 'banglore',
popu: '2000'
},
{
id: 7,
name: 'ohter city 1',
popu: '1500'
},
{
id: 8,
name: 'ohter city 2',
popu: '1500'
},
{
id: 9,
name: 'anohter city 1',
popu: '2200'
},
{
id: 10,
name: 'anohter city 2',
popu: '2200'
},
]
code: filters array based on what I need to apply the conditions:
const filterCity = [
{
filterType: 'name',
filterValue: 'amritsar'
},
{
filterType: 'popu',
filterValue: '1200'
}
]
solutions I've tried:-
code: solution 1:
const filteredList = citiesData.filter(item => {
return filterCity.filter(fItem => item[fItem.filterType] === fItem.filterValue).length
})
code: solution 2:
const filteredList = citiesData.filter(item => {
return filterCity.reduce((acc, val) => {
if(item[val.filterType] === val.filterValue) {
acc = true
}
return acc;
}, false)
})
code: result I'm getting:
[
{ id: 1, name: 'amritsar', popu: '1200' },
{ id: 3, name: 'phagwara', popu: '1200' }
]
it's giving me two objects because according to the filters array I'm searching for the name and popu fields. but the expected result should be:
[ { id: 1, name: 'amritsar', popu: '1200' } ]
because the name and popu is similar in that but in the second object the name is not the same.
I want the code to check all the conditions and then give me the result. right now it's working on the individual filter and individual array item.
so can anyone help me on this!!
so, it should be an AND filter (combining all conditions)?
res = citiesData.filter(d =>
filterCity.every(f => d[f.filterType] === f.filterValue))
for the OR filter (any condition), replace every with some.

TS/JS, Unable to sum more than one properties, using map set and get

I have been trying to create a summary of an array of objects where it's grouped by the value of one property and 2 or more properties should get summed.
But for some reason the way I am trying is only giving me 2 values the property I am grouping by and first property I am summing.
I am unable to sum the next property.
The array I am starting with
combinedItems
[
{
itemSi: 1,
productId: 'one',
taxableValue: 100,
taxValue: 10,
product: { id: 'one', productName: 'product one', taxId: 'one' },
tax: { id: 'one', taxName: 'tax one' }
},
{
itemSi: 2,
productId: 'two',
taxableValue: 100,
taxValue: 10,
product: { id: 'two', productName: 'product two', taxId: 'one' },
tax: { id: 'one', taxName: 'tax one' }
}
]
I need to be able to group by the taxName and sum the taxableValue and taxValue.
const summaryValues = new Map<any []>();
for(const {tax, taxableValue, taxValue} of combinedItems)
summaryValues.set(
tax.taxName,
(summaryValues.get(tax.taxName) || 0) + taxableValue,
(summaryValues.get(tax.taxName) || 0) + taxValue,
);
const summaries = [...summaryValues]
console.log(summaries);
const taxSummary = summaries.map(x => ({
taxName: x[0],
taxableValue: x[1],
taxValue: x[2]
}));
console.log(taxSummary)
The result I am getting
[ [ 'tax one', 200 ] ]
[ { taxName: 'tax one', taxableValue: 200, taxValue: undefined } ]
This is how the combined items are gotten:
const items: any[] = [
{
itemSi: 1,
productId: "one",
taxableValue: 100,
taxValue: 10
},
{
itemSi: 2,
productId: "two",
taxableValue: 100,
taxValue: 10
}
];
const products: any[] = [
{
id: "one",
productName:"product one",
taxId: "one"
},
{
id: "two",
productName:"product two",
taxId: "one"
}
]
const taxes: any[] = [
{
id: "one",
taxName:"tax one"
},
{
id: "two",
taxName:"tax two"
}
]
let combinedItems: any [] = []
combinedItems = items.map(x => {
let pdtItem = products.find(z => z.id === x.productId);
let taxItem = taxes.find(z => z.id === pdtItem.taxId);
let item = {...x, product: {...pdtItem }, tax: {...taxItem}};
return item;
});
console.log(combinedItems)
Map is a key-value store. What you're trying to do appears to be calling set with three arguments, whereas it only takes two (key and value).
If you need to produce multiple aggregations, you could store the results in an object:
const summaries = new Map();
for (const { tax: { taxName }, taxableValue, taxValue } of combinedItems) {
const currentSummary = summaries.get(taxName) || { taxableValue: 0, taxValue: 0 }
summaries.set(
taxName,
{ taxableValue: currentSummary.taxableValue + taxableValue, taxValue: currentSummary.taxValue + taxValue }
);
}

find an object in Array of Array

if i want for an example loop Through this Array to find specific item in Items Array how to approach that? i made this logic but it doesn't work
DATA.map((D)=>{
return D.items.find((item)=>{
return item.name ==='Blue Beanie'
})
})
this is the Array plus how to create new ONE array includes the both of items arrays to be like that: items: [{
id: 1,
name: 'Brown Brim',
price: 25
},
{
id: 2,
name: 'Blue Beanie',
price: 18
},
{
id: 3,
name: 'Adidas NMD',
price: 220
},
{
id: 4,
name: 'Adidas Yeezy',
price: 280
}
]
const DATA= [
{
id: 1,
title: 'Hats',
routeName: 'hats',
items: [
{
id: 1,
name: 'Brown Brim',
price: 25
},
{
id: 2,
name: 'Blue Beanie',
price: 18
}
]
},
{
id: 2,
title: 'Sneakers',
routeName: 'sneakers',
items: [
{
id: 3,
name: 'Adidas NMD',
price: 220
},
{
id: 4,
name: 'Adidas Yeezy',
price: 280
}
]
}
];
Transform DATA into list of items and find from that list your expected item
const res = DATA.flatMap((D) => D.items).find(
(item) => item.name === "Brown Brim"
)
const DATA = [
{
id: 1,
title: "Hats",
routeName: "hats",
items: [
{
id: 1,
name: "Brown Brim",
price: 25,
},
{
id: 2,
name: "Blue Beanie",
price: 18,
},
],
},
{
id: 2,
title: "Sneakers",
routeName: "sneakers",
items: [
{
id: 3,
name: "Adidas NMD",
price: 220,
},
{
id: 4,
name: "Adidas Yeezy",
price: 280,
},
],
},
]
const res = DATA.flatMap((D) => D.items).find(
(item) => item.name === "Brown Brim"
)
console.log(res)
Reference
Array.prototype.flatMap()
Maybe this is helpful?
const DATA= [
{id: 1,title:'Hats',routeName:'hats',
items:[{id: 1,name:"Brown Brim",price:25},
{id: 2,name: 'Blue Beanie',price: 18}]},
{id: 2,title: 'Sneakers',routeName: 'sneakers',
items: [{id: 3,name: 'Adidas NMD',price: 220},
{id: 4,name: 'Adidas Yeezy',price: 280}]}
];
console.log(DATA.map(D=>D.items.find(item=>item.name==='Brown Brim'))
.filter(e=>e))
The .map returns either an element matching your criterion or undefined, The chained .filter then removes all "falsy" elements, i. e. all the undefined ones.
As for the first question "loop Through this Array to find a specific item in Items Array"
given it is not sorted in any way, this can be done by iterating over the DATA array and search inside the items
If want to have access to the item from the outside of the 'forEach' scope you have to declare the variable outside
Regarding the second question, use the reduce function while iterating the array
NOTE: You can obviously combine both tasks as you already iterate through the array, so no need to do it twice. But to avoid confusion, I separated the logic.
Also, if you do choose to combine the tasks, using the reduce is not relevant, but very much like the answer to the first question, you can declare a buffer such as an array, and just copy items to it on the go (I'll leave out questions on performance for that matter)
const DATA = [
{
id: 1, title: 'Hats', routeName: 'hats',
items: [
{id: 1,name: 'Brown Brim',price: 25},
{id: 2,name: 'Blue Beanie',price: 18}
]
},
{
id: 2, title: 'Sneakers', routeName: 'sneakers',
items: [
{id: 3,name: 'Adidas NMD',price: 220},
{id: 4,name: 'Adidas Yeezy',price: 280}
]
}
];
//Question 1
//creating the object that will hold the item
//outside of the 'forEach' scope so we can use it later
let res = {};
const searchRes = DATA.forEach(entry => {
entry.items.forEach(item => {
if (item.name === 'Brown Brim')
//duplicating the item to a variable declared outside of this scope
res = { ...item
};
});
});
console.log(`Q1 - The item we found:`);
console.log(res);
// Question 2
// Merging all object inside the 'items' array using Reduce
const merged = DATA.reduce((acc, curr) =>
acc.concat(curr.items), []);
console.log(`Q2 - The merged array:`);
console.log(merged)

How to update 2D array with another 2D array matching on an id

I have two lists and would like to update one with the other on a matching index.
let cart = [{
header: some_data,
items: [{
id: 7,
properties: some_props_7,
time: 12345
}, {
id: 19,
properties: some_props_19,
time: 13344
}, {
id: 24,
properties: some_props_24,
time: 14342
}]
}, etc.];
let newData = [{
header: some_data,
items: [{
id: 19,
properties: some_new_props_19,
time: 17744
}, {
id: 24,
properties: some_new_props_24,
time: 18342
}]
}, etc.];
I am iterating over the cart, but am not coming up with an efficient way to update. I think I need another for loop in the "update cart" section, but that seems sub-optimal to me.
k is a list of indices
let i = 0, j = 0, l = cart.length;
for (i = 0; i < l; i++) {
let m = cart[i]['items'].length;
for (j = 0; j < m; j++) {
// update cart index with associated newData matching on id
}
}
How do I update the header and items data in the cart from a dynamic newData list?
Filter out overlaps and concatenate with new items:
let cart = {
header: 'some_data',
items: [{
id: 7,
properties: 'some_props_7',
time: 12345
}, {
id: 19,
properties: 'some_props_19',
time: 13344
}, {
id: 24,
properties: 'some_props_24',
time: 14342
}]
};
let newData = {
header: 'some_data',
items: [{
id: 19,
properties: 'some_new_props_19',
time: 17744
}, {
id: 24,
properties: 'some_new_props_24',
time: 18342
}]
};
//TEST
console.log(
cart.items.filter(item => !newData.items.map(i => i.id).includes(item.id)).concat(newData.items)
);
Updated in response to comments
It now maps properly over lists.
var cart = [{
header: 'some_data',
items: [{
id: 7,
properties: 'some_props_7',
time: 12345
}, {
id: 19,
properties: 'some_props_19',
time: 13344
}, {
id: 24,
properties: 'some_props_24',
time: 14342
}]
}];
var newData = [{
header: 'some_data',
items: [{
id: 19,
properties: 'some_new_props_19',
time: 17744
}, {
id: 24,
properties: 'some_new_props_24',
time: 18342
}]
}];
//TEST
cart = cart
//Modify existing data elements
.map(function(car) {
newData.forEach(function(data) {
if (data.header === car.header) {
car.items = car.items.filter(function(item) {
return !data.items.map(function(i) {
return i.id;
}).includes(item.id);
}).concat(data.items);
}
});
return car;
})
//Add new data elements
.concat(newData.filter(function(data) {
return !cart.some(function(car) {
return car.header === data.header;
});
}));
console.log(cart);

Merging/extend javascript object arrays based on join of a key property in each

I am wanting to merge the following object arrays, by first joining on the id property
var arr1 = [{
id: 1,
name: 'fred',
title: 'boss'
},{
id: 2,
name: 'jim',
title: 'nobody'
},{
id: 3,
name: 'bob',
title: 'dancer'
}];
var arr2 = [{
id: 1,
wage: '300',
rate: 'day'
},{
id: 2,
wage: '10',
rate: 'hour'
},{
id: 3,
wage: '500',
rate: 'week'
}];
So the result would be
[{
id: 1,
name: 'fred',
title: 'boss',
wage: '300',
rate: 'day'
},{
id: 2,
name: 'jim',
title: 'nobody',
wage: '10',
rate: 'hour'
},{
id: 3,
name: 'bob',
title: 'dancer',
wage: '500',
rate: 'week'
}]
I would like to avoid using js frameworks (if possible), although ExtJs is already part of the project.
AT the moment I have a loop with an inner loop that if the keys match it copies the properties and breaks out of the inner loop to start the next outer loop.
Any better suggestions?
Like this?
var combined = [];
function findSecond(id,second){
for (var i=0;i<second.length;i++){
if(second[i].id === id){
return second[i];
}
}
return null
}
while (el = arr1.pop()){
var getSec = findSecond(el.id,arr2);
if (getSec){
for (var l in getSec){
if (!(l in el)) {
el[l] = getSec[l];
}
}
combined.push(el);
}
}
If the arrays have the same length, and the id's are equal, a simpler merge will do:
function merge(a1,a2) {
var i = -1;
while ((i = i+1)<a1.length) {
for (var l in a2[i]) {
if (!(l in a1[i] )) {
a1[i][l] = a2[i][l];
}
}
}
return a1;
}
Here's a working example
[Edit 2016/07/30] Added a snippet using more functional approach and, based on #djangos comment, an extra method to combine both arrays.
(function() {
var alert = function(str) {document.querySelector('#result').textContent += str + '\n';};
var arrays = getArrays();
alert('Combine on id (shared id\'s):')
alert(JSON.stringify(combineById(arrays.arr1, arrays.arr2), null, ' '));
alert('\nCombine on id (all id\'s):')
alert(JSON.stringify(combineBothById(arrays.arr1, arrays.arr2), null, ' '));
// for combineBothById the parameter order isn't relevant
alert('\nCombine on id (all id\'s, demo parameter order not relevant):')
alert(JSON.stringify(combineBothById(arrays.arr2, arrays.arr1), null, ' '));
// combine first array with second on common id's
function combineById(arr1, arr2) {
return arr1.map(
function (el) {
var findInB = this.filter(function (x) {return x.id === el.id;});
if (findInB.length) {
var current = findInB[0];
for (var l in current) {
if (!el[l]) {el[l] = current[l];}
}
}
return el;
}, arr2);
}
// combine first array with second on all id's
function combineBothById(arr1, arr2) {
var combined = arr1.map(
function (el) {
var findInB = this.filter(function (x) {return x.id === el.id;});
if (findInB.length) {
var current = findInB[0];
for (var l in current) {
if (!el[l]) {el[l] = current[l];}
}
}
return el;
}, arr2);
combined = combined.concat(arr2.filter(
function (el) {
return !this.filter(function (x) {return x.id === el.id;}).length;
}, combined));
return combined;
}
function getArrays() {
return {
arr1: [{
id: 1,
name: 'fred',
title: 'boss'
}, {
id: 2,
name: 'jim',
title: 'nobody'
}, {
id: 3,
name: 'bob',
title: 'dancer'
}],
arr2: [{
id: 1,
wage: '300',
rate: 'day'
}, {
id: 2,
wage: '10',
rate: 'hour'
}, {
id: 4,
wage: '500',
rate: 'week'
}]
};
}
}());
<pre id="result"></pre>
You can merge two arrays by id column with Alasql library:
var res = alasql('SELECT * FROM ? arr1 JOIN ? arr2 USING id', [arr1,arr2]);
Try this example at jsFiddle.
try this...
var arr1 = [{
id: 1,
name: 'fred',
title: 'boss'
},{
id: 2,
name: 'jim',
title: 'nobody'
},{
id: 3,
name: 'bob',
title: 'dancer'
}];
var arr2 = [{
id: 1,
wage: '300',
rate: 'day'
},{
id: 2,
wage: '10',
rate: 'hour'
},{
id: 3,
wage: '500',
rate: 'week'
}];
let arr5 = arr1.map((item, i) => Object.assign({}, item, arr2[i]));
console.log(arr5)

Categories

Resources