Combine multiple arrays and convert it to another object array in Angular - javascript

I saw many similar questions like this. But none of them helped me to fulfill my need. So I'm posting this question.
I have multiple arrays as per the users' choice. As an example, I will use 2 arrays here.
color = [{id: 1, name: "Red"}, {id: 2, name: "Green"}, {id: 1, name: "Blue"}]
size = [{id: 1, name: "Small"}, {id: 2, name: "Medium"}]
I want to get all possible combinations of given arrays and add some keys on top of that as an output.
My expected output is something like the below.
[{"color": "Red", "size": "Small", "price":0, "Quantity": 0},
{"color": "Red", "size": "Medium", "price":0, "Quantity": 0},
{"color": "Green", "size": "Small", "price":0, "Quantity": 0},
{"color": "Green", "size": "Medium", "price":0, "Quantity": 0},
{"color": "Blue", "size": "Small", "price":0, "Quantity": 0},
{"color": "Blue", "size": "Medium", "price":0, "Quantity": 0}]
If the user gives 3 arrays then it should create the combination accordingly but the properties "price" and "Quantity" will be added to the combination.
Please suggest to me how can I achieve this in Angular?

Just go through the lists and build the objects:
const colors = [{id: 1, name: "Red"}, {id: 2, name: "Green"}, {id: 1, name: "Blue"}]
const sizes = [{id: 1, name: "Small"}, {id: 2, name: "Medium"}]
const otherProps = {price:0, Quantity: 0}
const res = colors.flatMap(color => sizes.map(size => ({color: color.name, size: size.name, ...otherProps})))
console.log(res)
If you have an arbitrary number of arrays, you need to find a way to specify keys for it, for example by putting them into an object:
const data = {
color: [{id: 1, name: "Red"}, {id: 2, name: "Green"}, {id: 1, name: "Blue"}],
sizes: [{id: 1, name: "Small"}, {id: 2, name: "Medium"}],
stuff: [{name: 'foo'}, {name: 'bar'}]
}
const otherProps = {price:0, Quantity: 0}
const result = Object.entries(data)
.map( ([propName, values]) => values.map(v =>({[propName]: v.name}))) // build the key-value pairs, i.e. [{color: 'Red'}, ...]
.reduce( (groups, props) => groups.length === 0 ? props :
groups.flatMap(group => props.map(prop => ({...group, ...prop, ...otherProps}))) // add new properties to existing groups
);
console.log(result)

Related

Most efficient way to remove objects from array based on key value objects in another array

Given the excludes and items arrays, I want to return an array of objects from items that don't contain a key, value pair corresponding to a key, value pair object in excludes. Is there a better way of solving this other than using nested for loops?
const excludes = [{key: "color", value: "Red"}, {key: "age", value:12}, {key:"score", value: 75}];
const items = [{color: "Red", score: 30, age: 12}, {color: "Blue", score: 100, age: 20}, {color: "Red", score: 75, age: 30}];
//Expected output: [{color: "Blue", score: 100, age: 20}]
You can use Array#filter, as suggested by #Barmar, in combination with Array#every as follows:
const excludes = [{key: "color", value: "Red"}, {key: "age", value:12}, {key:"score", value: 75}],
items = [{color: "Red", score: 30, age: 12}, {color: "Blue", score: 100, age: 20}, {color: "Red", score: 75, age: 30}],
output = items.filter(
item => excludes.every(
({key,value}) => item[key] !== value
)
);
console.log( output );
//OUTPUT: [{color: "Blue", score: 100, age: 20}]
Use filter() to filter the items array. The filtering criteria checks each of the properties of the item against the excludes array. If none of them match, the item is included in the result.
const excludes = [{key: "color", value: "Red"}, {key: "age", value:12}, {key:"score", value: 75}];
const items = [{color: "Red", score: 30, age: 12}, {color: "Blue", score: 100, age: 20}, {color: "Red", score: 75, age: 30}];
let result = items.filter(item =>
!Object.entries(item).some(([key, value]) =>
excludes.find(e => e.key = key && e.value == value)));
console.log(result);

I want to store a flat array of object into a nested object form (like tree format)

I want to store a flat array of object into a nested object form (like tree format).
I found a solution for it but can't find how does variable "root" get all the values.
Can someone help me with this understanding.
var data = [
{id: 10, Name: "a", parentId: null},
{id: 20, Name: "b", parentId: 10},
{id: 30, Name: "c", parentId: 10},
{id: 40, Name: "d", parentId: 20},
{id: 50, Name: "e", parentId: 20},
{id: 60, Name: "f", parentId: 30},
{id: 70, Name: "g", parentId: 30},
{id: 80, Name: "h", parentId: 50},
{id: 90, Name: "i", parentId: 60},
{id: 100, Name: "j", parentId: 70}
];
const idMapping = data.reduce((acc, rec, i) => {
acc[rec.id] = i;
return acc;
},{});
//console.log("idMapping=",idMapping);
let root;
data.forEach(record => {
// Handle the root element
if (record.parentId === null || record.parentId === "") {
root = record;
return;
}
// Use our mapping to locate the parent element in our data array
const parentEl = data[idMapping[record.parentId]];
// Add our current record to its parent's `children` array
parentEl.children = [...(parentEl.children || []), record];
});
console.log("root::",root);
Output is like::
root::
{
id:10,
Name:"a",
parentId:null,
children:(2) [
{
id:20,
Name:"b",
parentId:10,
children:(2) [...]
},
{
id:30,
Name:"c",
parentId:10,
children:(2) [...]
}
]
}
Also, a better solution will also be appreciated. Thanks

How to get objects with min values by category

I believe I have an array of javascript objects as follows, and I am trying to filter out all but the lowest prices for each item:
Have:
const fruits = [
{id: 1, fruit: "apple", store: "store1", price: 1},
{id: 2, fruit: "apple", store: "store2", price: 1.25},
{id: 3, fruit: "banana", store: "store1", price: 0.5},
{id: 4, fruit: "banana", store: "store2", price: 0.75},
{id: 5, fruit: "banana", store: "store3", price: 0.75}
];
Want:
[
{id: 1, fruit: "apple", store: "store1", price: 1},
{id: 3, fruit: "banana", store: "store1", price: 0.5}
];
I imagine in SQL this would be accomplished with a group by statement such as "select fruit, min(price) from table group by fruit", but am not sure how I might accomplish this in its current object form.
I am looking for a JavaScript solution but am open to changing the data structure or using a framework if needed. I am already using React.js if that is relevant. Any guidance?
object assign and reduce:
const fruits =
[ { id: 1, fruit: "apple", store: "store1", price: 1 }
, { id: 2, fruit: "apple", store: "store2", price: 1.25 }
, { id: 3, fruit: "banana", store: "store1", price: 0.5 }
, { id: 4, fruit: "banana", store: "store2", price: 0.75 }
, { id: 5, fruit: "banana", store: "store3", price: 0.75 }
]
const mini = fruits.reduce((a,c)=>
{
let x = a.find(e=>e.fruit===c.fruit)
if (!x) a.push(Object.assign({},c))
else if (x.price > c.price) Object.assign(x,c)
return a
},[])
console.log( mini )
– Dave Newton : . With a map/object-based solution you iterate fruits once and let the result object handle lookups.
there is an other way for this :
const fruits =
[ { id: 1, fruit: "apple", store: "store1", price: 1 }
, { id: 2, fruit: "apple", store: "store2", price: 1.25 }
, { id: 3, fruit: "banana", store: "store1", price: 0.5 }
, { id: 4, fruit: "banana", store: "store2", price: 0.75 }
, { id: 5, fruit: "banana", store: "store3", price: 0.75 }
]
const mini = fruits.reduce((a,c,i,t)=>
{
if (!a.find(e=>e.fruit===c.fruit))
a.push(t.filter(e=>e.fruit===c.fruit).reduce((r,n)=>r.price<n.price?r:n) )
return a
},[])
console.log( mini )
You can do this via a reduce operation, creating a map of category (fruit) to lowest price records.
For example
const fruits = [
{id: 1, fruit: "apple", store: "store1", price: 1},
{id: 2, fruit: "apple", store: "store2", price: 1.25},
{id: 3, fruit: "banana", store: "store1", price: 0.5},
{id: 4, fruit: "banana", store: "store2", price: 0.75},
{id: 5, fruit: "banana", store: "store3", price: 0.75}
]
const result = [...fruits.reduce((map, entry) => {
// check for an existing record and if it has a greater price
if (!map.has(entry.fruit) || map.get(entry.fruit).price > entry.price) {
// set the new record for this category
map.set(entry.fruit, entry)
}
return map
}, new Map()).values()] // now just get the values
console.info(result)
If there are multiple entries with the same price, this will keep the first one found. If you want to keep the last, use a >= comparison instead.
groupBy is a good commonly needed operation to have handy. I'd use that first to group the objects by fruit. The second reduce (minInArray) also is a commonly used form to find min or max in an array. Together they produce the min for each fruit type...
const groupBy = (array, key) => {
return array.reduce(function(r, a) {
r[a[key]] = r[a[key]] || []
r[a[key]].push(a)
return r
}, {})
}
const minInArray = (array, key) => {
return array.reduce((prev, curr) => {
return prev[key] < curr[key] ? prev : curr
})
}
const fruits = [{
id: 1,
fruit: "apple",
store: "store1",
price: 1
}, {
id: 2,
fruit: "apple",
store: "store2",
price: 1.25
}, {
id: 3,
fruit: "banana",
store: "store1",
price: 0.5
}, {
id: 4,
fruit: "banana",
store: "store2",
price: 0.75
}, {
id: 5,
fruit: "banana",
store: "store3",
price: 0.75
}];
let groups = groupBy(fruits, 'fruit')
let minPrices = Object.keys(groups).map(fruit => {
return minInArray(groups[fruit], 'price')
})
console.log(minPrices)

Combine 2 objects from 2 arrays in a loop?

I'm creating a deck of cards (an array of card objects) by combining an array of suit objects, and an array of card objects, using javascript.
I'm using a forEach loop to loop through the suits, and nested is a map loop for the cards.
The console.log is returning the correct object to push to the new array, however the .push() is only appending a combined object using the last suit and the last card.
Where am I going wrong with this?
I have tried multiple different loops and methods to append to a new array without luck.
Console.log() returns the correct value, but I am unable to push the correct combined objects to a new array.
// Deck of Cards
var suits = [
{ suit: "clubs", color: "black" },
{ suit: "spades", color: "black" },
{ suit: "hearts", color: "red" },
{ suit: "diamonds", color: "red" }
];
var family = [
{ name: "2", value: 2 },
{ name: "3", value: 3 },
{ name: "4", value: 4 },
{ name: "5", value: 5 },
{ name: "6", value: 6 },
{ name: "7", value: 7 },
{ name: "8", value: 8 },
{ name: "9", value: 9 },
{ name: "10", value: 10 },
{ name: "J", value: 10 },
{ name: "Q", value: 10 },
{ name: "K", value: 10 },
{ name: "A", value: 1 },
];
var deck = new Array();
suits.forEach(function (x) {
var arr = family.map((y) => {
var obj = Object.assign(x, y);
console.log(obj);
deck.push(obj);
return obj;
});
console.log(arr);
});
console.log(deck);
try Object.assign({}, x, y) instead of Object.assign(x, y). Currently you're manipulating the object that is x, by adding all properties of y to it.
// Deck of Cards
var suits = [
{suit: "clubs",color: "black"},
{suit: "spades",color: "black"},
{suit: "hearts",color: "red"},
{suit: "diamonds",color: "red"}
];
var family = [
{name: "2",value: 2},
{name: "3",value: 3},
{name: "4",value: 4},
{name: "5",value: 5},
{name: "6",value: 6},
{name: "7",value: 7},
{name: "8",value: 8},
{name: "9",value: 9},
{name: "10",value: 10},
{name: "J",value: 10},
{name: "Q",value: 10},
{name: "K",value: 10},
{name: "A",value: 1},
];
var deck = new Array();
suits.forEach(function(x){
var arr = family.map( (y) => {
var obj = Object.assign({}, x, y);
deck.push(obj);
return obj;
});
});
console.log(deck);
Object.assign(x,y) will put the values of y onto x. You want to leave x alone, so store your properties in a new Object using Object.assign({}, x,y). Consider the demo below:
var suits = [
{suit: "clubs",color: "black"},
{suit: "spades",color: "black"},
{suit: "hearts",color: "red"},
{suit: "diamonds",color: "red"}
];
var family = [
{name: "2",value: 2},
{name: "3",value: 3},
{name: "4",value: 4},
{name: "5",value: 5},
{name: "6",value: 6},
{name: "7",value: 7},
{name: "8",value: 8},
{name: "9",value: 9},
{name: "10",value: 10},
{name: "J",value: 10},
{name: "Q",value: 10},
{name: "K",value: 10},
{name: "A",value: 1},
];
const tmp = suits.reduce((acc, s) => {
return acc.concat(family.map(f => {
return Object.assign({}, f, s);
}));
}, []);
const pre = document.createElement("pre");
pre.innerHTML = JSON.stringify(tmp, null, 4);
document.body.appendChild(pre);
If you can use flatmap, then it can be made significantly simpler:
const suits = [{ suit: "clubs", color: "black" }, { suit: "spades", color: "black" }, { suit: "hearts", color: "red" }, { suit: "diamonds", color: "red" }]
const family = [{ name: "2", value: 2 }, { name: "3", value: 3 }, { name: "4", value: 4 }, { name: "5", value: 5 }, { name: "6", value: 6 }, { name: "7", value: 7 }, { name: "8", value: 8 }, { name: "9", value: 9 }, { name: "10", value: 10 }, { name: "J", value: 10 }, { name: "Q", value: 10 }, { name: "K", value: 10 }, { name: "A", value: 1 }]
const deck = suits.flatMap(s => family.map(f => ({...s, ...f})))
console.log(deck)
A side note, there seems to be a strong convention (presumably from Bridge) of ordering the suits clubs/diamonds/hearts/spades. In English that's easy to remember since they're alphabetic.)

How to filter array by object value and return a new array?

I am having a hard time in finding a way to filter this array by id value, and generate a new one preserving coords with it's filter.
Array example:
var herbs =[
{
"coords":[3300,2796],"items":[
{id: "dandelion",qty: 72},
{id: "sage",qty: 4},
{id: "valerian",qty: 1},
]},
{
"coords":[3300,2800],"items":[
{id: "dandelion",qty: 26},
{id: "valerian",qty: 7},
{id: "sage",qty: 2},
]},
{
"coords":[3300,2804],"items":[
{id: "dandelion",qty: 57},
{id: "sage",qty: 4},
{id: "wormwood",qty: 1},
]}]
I want to filter it by id, generating a new one with it's coords.
Example:
Filtering by id = dandelion
var dandelion =[
{
"coords":[3300,2796],"items":[
{id: "dandelion",qty: 72},
]},
{
"coords":[3300,2800],"items":[
{id: "dandelion",qty: 26},
]},
{
"coords":[3300,2804],"items":[
{id: "dandelion",qty: 57},
]}]
Filtering by id = sage
var sage =[
{
"coords":[3300,2796],"items":[
{id: "sage",qty: 4},
]},
{
"coords":[3300,2800],"items":[
{id: "sage",qty: 2},
]},
{
"coords":[3300,2804],"items":[
{id: "sage",qty: 4},
]}]
Also, this array it's pretty big, I have 467.000 coords. So I plan to filter it and save a new file with each filtered.
You can use reduce for this to push to a new array with items the result of a filter within the reduce call. It only pushes to the new array when the search term is found somewhere in the items:
var herbs =[
{
"coords":[3300,2796],"items":[
{id: "dandelion",qty: 72},
{id: "sage",qty: 4},
{id: "valerian",qty: 1},
]},
{
"coords":[3300,2800],"items":[
{id: "dandelion",qty: 26},
{id: "valerian",qty: 7},
{id: "sage",qty: 2},
]},
{
"coords":[3300,2804],"items":[
{id: "dandelion",qty: 57},
{id: "sage",qty: 4},
{id: "wormwood",qty: 1},
]}]
function filterByID(array, id) {
return array.reduce((a, c) => {
let items = c.items.filter(i => i.id === id )
if (items.length){
a.push({
coords: c.coords,
items: items
})
}
return a
}, [])
}
console.log(filterByID(herbs, "dandelion"))
console.log(filterByID(herbs, "sage"))
It is very straightforward. Use standard Array methods map and filter to get what you want.
var herbs = [{
"coords": [3300, 2796],
"items": [{
id: "dandelion",
qty: 72
},
{
id: "sage",
qty: 4
},
{
id: "valerian",
qty: 1
},
]
},
{
"coords": [3300, 2800],
"items": [{
id: "dandelion",
qty: 26
},
{
id: "valerian",
qty: 7
},
{
id: "sage",
qty: 2
},
]
},
{
"coords": [3300, 2802],
"items": [{
id: "dandelion",
qty: 26
},
{
id: "valerian",
qty: 7
},
//no sage
]
},
{
"coords": [3300, 2804],
"items": [{
id: "dandelion",
qty: 57
},
{
id: "sage",
qty: 4
},
{
id: "wormwood",
qty: 1
},
]
}
];
function filterHerbs(id) {
return herbs.map((h) => {
return {
coords: h.coords,
items: h.items.filter((i) => i.id == id)
}
})//filter result in case of empty item arrays
.filter((h) => h.items.length);
}
var dand = filterHerbs('dandelion')
console.log(dand);
var sage = filterHerbs('sage');
console.log(sage);
const dandelion = herbs.map(
({coords, items}) => items.filter(
({id}) => id === 'dandelion'
).map(ob => ({coords, ...ob})));

Categories

Resources