Compare object array with object and reduce quantity - javascript

I have an object containing an id as the key, and quantity as the value, e.g.;
let order = {1002: 2, 1010: 1}
I want to compare the order object with an array of products, e.g.:
let stock = [{name: "test", id: "1002", quantity: 100}, {name: "moreTest", id: "1010", quantity: 100}]
I want to reduce the quantity for each object in my stocks array, based on the according value in the stocks object, e.g.:
let newStock = [{name: "test", id: "1002", quantity: 98}, {name: "moreTest", id: "1010", quantity: 99}]

Here's a one-liner approach:
let newStock = stock.map(item => ({...item, quantity: item.quantity - (order[parseInt(item.id,10)] || 0)}))
BTW:
I think stock's id should be a number (or order needs to have a string key - I used parseInt but I don't think it's the best practice).
stock should be a map as well to prevent multiple stocks with the same id.

Here is a possible solution, very clear:
let order = {1002: 2, 1010: 1}
let stock = [{name: "test", id: "1002", quantity: 100}, {name: "moreTest", id: "1010", quantity: 100}]
stock.forEach(element => {
Object.keys(order).forEach(function(key) {
const quantityBought = order[key];
// == and not === because element.id is a string and key is a number
if (element.id == key) {
element.quantity -= quantityBought;
}
});
});
console.log(stock);

You can use Array.prototype.reduce here something Like this
let stock = [{name: "test", id: "1002", quantity: 100}, {name: "moreTest", id: "1010", quantity: 100}]
let order = {1002: 2, 1010: 1}
let result=stock.reduce((acc,value)=>{
value.quantity=value.quantity-order[value.id];
acc.push(value);
return acc;
},[])
console.log(result)
For reference look at array reduce function in mozilla docs

Related

How to group array of object by key value pairs using javaScript?

I just started learning JavaScript, I have this type of array, how I can turn this array of objects into key-value pairs like below, Any source and reference is acceptable.
Sample Array:
[
{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},
{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}
]
Expected Result:
{
"6d7e75e6-c58b-11e7-95-ac162d77eceb":1,
"6d2e75e6-c58b-11e7-95-ac162d77eceb":1
}
Using Array.prototype.Reduce:
const arr = [{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}];
const result = arr.reduce((acc, { Id, qty }) => ({ ...acc, [Id]: qty }), {});
console.log(result);
Another approach, a little more beginner friendly.
const arr = [
{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},
{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}
];
const newObject = {}; // empty object
// loop over each element of the array
arr.forEach(element => {
// the key is the element identifier (Id) and the value is the element quantity (qty)
newObject[element.Id] = element.qty;
});
You can use a loop and add the item.Id as the key and the item.qty as the value in an empty object.
let arr = [{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}]
let obj = {}
arr.forEach(item => {
obj[item.Id] = item.qty
})
console.log(obj)
You can easily achieve this result using forEach in a single line of code.
const arr = [
{ Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 },
{ Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 },
];
const result = {};
arr.forEach(({ Id, qty }) => (result[Id] = qty));
console.log(result);
You can achieve the desired result with below code
//input array
const arrList = [
{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},
{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}
]
function findArray(arr) {
//define a new array to store Id's
let newArray = [];
//iterate through array items
arr.forEach(item => {
newArray.push(item.Id);
});
return newArray;
}
//call findArray function to get desired output
console.log(findArray(arrList));
Using Object.fromEntries()
const
array = [{ Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 }, { Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 }],
object = Object.fromEntries(array.map(({ Id, qty }) => [Id, qty]));
console.log(object);
or, for some fragile novelty...
const
array = [{ Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 }, { Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 }],
object = Object.fromEntries(array.map(Object.values));
console.log(object);

Filter array of objects but return only specific properties - JS [duplicate]

This question already has answers here:
Extract certain properties from all objects in array
(5 answers)
Closed 4 months ago.
How to filter an array of objects with a condition and return only specific properties of filtered objects?
I know we can use filter followed by map to achieve this. But I am looking for more simple solution.
For ex:
let arr = [{name:"lala", id: 1}, {name: "coco", id:2}, {name: "lala", id:3}]
Suppose if I want only ids of name "lala".
Output should be,
[{id: 1}, {id: 3}]
The next simplest would be reduce
let arr = [{name:"lala", id: 1}, {name: "coco", id:2}, {name: "lala", id:3}];
console.log(
arr.reduce((values, value) =>
{
if (value.name === 'lala') values.push({ id: value.id });
return values;
}, [])
);
You can simply use Array.prototype.reduce to combine both mapping and filtering in the same operation. If you want to make it super concise, you can use object destructuring in the second argument of the reduce callback:
let arr = [{name:"lala", id: 1}, {name: "coco", id:2}, {name: "lala", id:3}];
let filteredMappedArr = arr.reduce((acc, { name, id }) => {
if (name === 'lala')
acc.push({ id });
return acc;
}, []);
console.log(filteredMappedArr);
filter followed by map is probably the most readable solution, but if you're looking to do it all in one step, you're looking at the classic for loop or using reduce.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
You can do it by using filter and map;
let arr = [{name:"lala", id: 1}, {name: "coco", id:2}, {name: "lala", id:3}]
let res = arr.filter(item => item.id % 2 === 1).map(item => ({id: item.id}))
console.log(res);
You could take Array#flatMap and return either a new obejct or an empty array which has no value for flattening.
let array = [{ name: "lala", id: 1 }, { name: "coco", id: 2 }, { name: "lala", id: 3 }],
result = array.flatMap(({ id, name }) => name === 'lala' ? [{ id }] : []);
console.log(result);
using .filter() and .map() functions:
let arr = [{name:"lala", id: 1}, {name: "coco", id:2}, {name: "lala", id:3}]
let newArr = arr.filter((elm) => (elm.name === 'lala')).map( (elm) => {return {id:elm.id}});
console.log(newArr);
let arr = [
{ name: "lala", id: 1 },
{ name: "coco", id: 2 },
{ name: "lala", id: 3 },
];
let a = [];
arr.filter(({ name, id }) => {
if (name === "lala") {
a.push({ id });
}
});
console.log(a);
with filter we check for the condition where name matches 'lala' if yes then we push id to new array...that's simple

Merge two arrays of objects by ID

I have two arrays like this:
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
Now I want a result like this:
result = [ {name: "apple", prices: [{id: 1, price: 50}, {id: 1, price: 40}]}, {name: "orange", prices: [{id: 2, price: 30}]}, {name: "others", prices: [{id: null, price: 80}]}]
I want to map the elements of the array a to the name of the second array b on the basis of their ids.
Here's an approach that using reduce to build a lookup set and avoid repeated searches in b. Another reduction pass builds the result arrays by name using the lookup table. Lastly, map is used to format the result.
Time complexity is linear (three passes with a lot of constant time object lookups).
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
const lookup = b.reduce((a, e) => {
a[e.id] = e.name;
return a;
}, {});
const result = Object.entries(
a.reduce((a, e) => {
const key = lookup[e.id] || "others";
if (!(key in a)) {
a[key] = [];
}
a[key].push(e);
return a;
}, {})
).map(e => ({name: e[0], prices: e[1]}));
console.log(result);
It would be more logical to not repeat the id in the prices part of the result, since the id belongs with the name.
I would suggest using a temporary map (for efficiency):
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
const map = new Map(b.map(o => [o.id, Object.assign(o, { prices: [] })]))
.set(null, {id: null, name: "others", prices: []});
a.forEach(o => map.get(o.id).prices.push(o.price));
const result = [...map.values()];
console.log(result);
Yes, you can do it simply with a map and a filter
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
b.map(({ name, id }) => ({
name,
id,
prices: a.filter(item => item.id === id).map(({ price }) => price)
}));
You can do this with a single Array.reduce and Object.values if you start by combining the 2 arrays together:
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
const result = Object.values([...b, ...a].reduce((r, c) => {
if ('name' in c || c.id == null)
r[c.id || 'others'] = ({name: c.name || 'others', prices: []})
if ('name' in c)
return r
else if (c.id != null)
r[c.id].prices.push(c)
else
r['others'].prices.push(c)
return r
}, {}))
console.log(result)
The idea is to start with the one containing the names so the grouping creates first the object groupings and then just fill the group arrays.

Unable to get index of an object in array

I thought if I maintain the original reference than I would simply get the index
const selected = {id: "abcd123", quantity: "5"};
const location = [
{id: "abcd123", quantity: "3"},
{id: "abcd1234", quantity: "3"},
];
const filterLocation = location.filter(loc => loc.id === selected.id);
console.log(location.indexOf(filterLocation));
I expect it would log 0 but it always return -1. How it actually works?
First of all the filterLocation does not include any object identical to the location. It includes the only object with the same id field but the quantity field is different. Secondly the indexOf method does not work properly for non-scalar arguments.
const selected = {id: "abcd123", quantity: "5"};
const location = [
{id: "abcd123", quantity: "3"},
{id: "abcd1234", quantity: "3"},
];
const filterLocation = location.filter(loc => loc.id === selected.id);
console.log(location.findIndex(value => value.id === filterLocation[0].id )));
filter return a new array. So, you just need access to that value filtered in this example
change indexOf for findIndex. location is an array of objects so you need to iterate all the array again for recover the index. Sure, it's just for this example, also you could recover the index in the same filter operation.
console.log(location.findIndex(value => value.id === filterLocation[0].id )));
Sorry my mistake
I think I should switch to findIndex instead of indexOf.
const selected = {id: "abcd123", quantity: "5"};
const location = [
{id: "abcd123", quantity: "3"},
{id: "abcd1234", quantity: "3"},
];
const filterLocation = location.filter(loc => loc.id === selected.id);
console.log(location.findIndex(loc => loc.id === selected.id));
Then I would get the specified index that I need.

Javascript object, access related keys

I have the following Javascript object:
var icecreams = [{
name: 'vanilla',
price: 10,
rating: 3
}, {
name: 'chocolate',
price: 4,
rating: 8
}, {
name: 'banana',
price: 1,
rating: 1
}, {
name: 'greentea',
price: 5,
rating: 7
}, {
name: 'moosetracks',
price: 6,
rating: 2
}, ];
I need to access the "related properties" (not sure of the exact terminology) in each section of the object. For example, if I am given the name "vanilla," I need to access a "price" of 10 and "rating" of 3. Is there a way to do this without changing the object's structure? Possibly using this?
You can use Array.prototype.filter()
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
var vanilla = icecreams.filter(function(o){
return o.name === 'vanilla'
});
//As filter return's an array you need to use index thus [0] to access first element of array
console.log(vanilla[0].price);
console.log(vanilla[0].rating);
In ECMAScript 2015 you can find object in your array like this:
var vanilla = icecreams.find(o => o.name === 'vanilla')
console.log(vanilla.price);
console.log(vanilla.rating);
ES5 analogy:
var vanilla = icecreams.reduce(function(r, o) {
return r || (o.name === 'vanilla' ? o : undefined);
}, undefined);
console.log(vanilla.price);
console.log(vanilla.rating);
var icecreams = [ {name: 'vanilla', price: 10, rating: 3}, {name: 'chocolate', price: 4, rating: 8}, {name: 'banana', price: 1, rating: 1}, {name: 'greentea', price: 5, rating: 7}, {name: 'moosetracks', price: 6, rating: 2}, ];
var found = {};
for(i in icecreams){
if(icecreams[i].name == 'vanilla'){
found = icecreams[i];
break;
}
}
console.log('Price: ' + found.price);
console.log('Price: ' + found.rating);
if you have control over the structure of the variable icecream, I'd uses an oject instead of an array to hold the flavors. This makes it simple to access the values by name.
var icecream = {
vanilla: {price: 10, taring: 3},
banana: {price: 1, taring: 1},
greentea: {price: 5, taring: 7},
moosetracks: {price: 6, taring: 2}
};
Or I'd probably adapt them if they came via AJAX (or another dynamic source):
//Assume icecream follows your original structure.
function tranformFlavors(flavors) {
var flavorObj = {};
flavors.forEach(item){
flavorObj[item.name] = item;
delete flavorObj[item.name].name;
}
return flavorObj;
}
icecream = transformFlavors(icecream);
If you're using ES5, you can use underscore or lodash for this.
http://underscorejs.org/#find
or
http://underscorejs.org/#filter
or
http://underscorejs.org/#where
or
http://underscorejs.org/#findWhere

Categories