Find entry from nested arrays of object javascript - javascript

I need to return the string with category and type together when use input the type id(which is unique) , I am pretty much done with that but I am unable to fetch root category .
My solution return 'Terrace' but I need 'Rural / Terrace'.
I think I am making things over complicated
let obj =[
{
category: 'Residential',
category_id: 1,
types: [{name: 'House', id: 1},{name: 'Townhouse / Villa', id: 3},{name: 'House and Land', id: 5},{name: 'Duplex', id: 4},{name: 'Land', id: 6},{name: 'Terrace', id: 16},{name: 'Apartment/Unit', id: 2},{name: 'Relocatable', id: 7}]
},
{
category: 'Commercial',
category_id: 2,
types: [{name: 'Office', id: 12},{name: 'House', id: 8},{name: 'Land', id: 9},{name: 'Shop', id: 10},{name: 'Factory', id: 11}]
},
{
category: 'Rural',
category_id: 3,
types: [{name: 'House', id: 17},{name: 'House and Land', id: 14},{name: 'Land', id: 13},{name: 'Terrace', id: 15}]
}
];
console.log(obj.map(o=>o.types).reduce((acc, val) => acc.concat(val), []).find(inner_obj=>inner_obj.id==15).name);

I might try something like this. Use reduce() to go through the main items, then use find() to see if you have a matching item. If so, push what you need to the array.
let obj =[{category: 'Residential',category_id: 1,types: [{name: 'House', id: 1},{name: 'Townhouse / Villa', id: 3},{name: 'House and Land', id: 5},{name: 'Duplex', id: 4},{name: 'Land', id: 6},{name: 'Terrace', id: 16},{name: 'Apartment/Unit', id: 2},{name: 'Relocatable', id: 7}]}, {category: 'Commercial',category_id: 2,types: [{name: 'Office', id: 12},{name: 'House', id: 8},{name: 'Land', id: 9},{name: 'Shop', id: 10},{name: 'Factory', id: 11}]},{category: 'Rural',category_id: 3,types: [{name: 'House', id: 17},{name: 'House and Land', id: 14},{name: 'Land', id: 13},{name: 'Terrace', id: 15}]}];
let found = obj.reduce((a, c) => {
let found = c.types.find(t => t.id === 15)
if (found) a.push({category: c.category, name:found.name})
return a
}, [])
console.log(found)
I could potentially return more than one item if there was more than one item with id===15. In this case it's not clear what should happen, but returning them all seems reasonable.
EDIT: if you know id is unique
If you know there is only one of each id, you can exit early, which might save some time in big searches:
let obj =[{category: 'Residential',category_id: 1,types: [{name: 'House', id: 1},{name: 'Townhouse / Villa', id: 3},{name: 'House and Land', id: 5},{name: 'Duplex', id: 4},{name: 'Land', id: 6},{name: 'Terrace', id: 16},{name: 'Apartment/Unit', id: 2},{name: 'Relocatable', id: 7}]}, {category: 'Commercial',category_id: 2,types: [{name: 'Office', id: 12},{name: 'House', id: 8},{name: 'Land', id: 9},{name: 'Shop', id: 10},{name: 'Factory', id: 11}]},{category: 'Rural',category_id: 3,types: [{name: 'House', id: 17},{name: 'House and Land', id: 14},{name: 'Land', id: 13},{name: 'Terrace', id: 15}]}];
function find(id, obj) {
for (prop of obj){
let found = prop.types.find(t => t.id === id)
if (found) return [prop.category, found.name]
}
}
console.log(find(15, obj))

Why not just something simple like this?
let obj = [{ category: 'Residential', category_id: 1, types: [{ name: 'House', id: 1 }, { name: 'Townhouse / Villa', id: 3 }, { name: 'House and Land', id: 5 }, { name: 'Duplex', id: 4 }, { name: 'Land', id: 6 }, { name: 'Terrace', id: 16 }, { name: 'Apartment/Unit', id: 2 }, { name: 'Relocatable', id: 7 }] }, { category: 'Commercial', category_id: 2, types: [{ name: 'Office', id: 12 }, { name: 'House', id: 8 }, { name: 'Land', id: 9 }, { name: 'Shop', id: 10 }, { name: 'Factory', id: 11 }] }, { category: 'Rural', category_id: 3, types: [{ name: 'House', id: 17 }, { name: 'House and Land', id: 14 }, { name: 'Land', id: 13 }, { name: 'Terrace', id: 15 }] } ];
for (let elem of obj) {
const typeFound = elem.types.find(type => type.id === 15);
if (typeFound) {
console.log(elem.category + " " + typeFound.name);
break;
}
}

Here's one way to do it. First, flatten the possible results to an array of results like [{id: 15, name: "Rural / Terrace"}, ...], then find the specific element you want.
let obj =[
{
category: 'Residential',
category_id: 1,
types: [{name: 'House', id: 1},{name: 'Townhouse / Villa', id: 3},{name: 'House and Land', id: 5},{name: 'Duplex', id: 4},{name: 'Land', id: 6},{name: 'Terrace', id: 16},{name: 'Apartment/Unit', id: 2},{name: 'Relocatable', id: 7}]
},
{
category: 'Commercial',
category_id: 2,
types: [{name: 'Office', id: 12},{name: 'House', id: 8},{name: 'Land', id: 9},{name: 'Shop', id: 10},{name: 'Factory', id: 11}]
},
{
category: 'Rural',
category_id: 3,
types: [{name: 'House', id: 17},{name: 'House and Land', id: 14},{name: 'Land', id: 13},{name: 'Terrace', id: 15}]
}
];
const flatTypes = obj.map(cat =>
cat.types.map(type => {
return {
id: type.id,
name: `${cat.category} / ${type.name}`
};
})
).reduce((flatTypes, typeArray) =>
flatTypes.concat(typeArray)
, []);
const result = flatTypes.find(type => type.id === 15).name;
console.log(result);

Related

Question from a beginner: Unexpected JS behavior [duplicate]

I'm trying to convert an array of objects where i return duplicated objects if the object properties quantity is greater than 1.
const objects = [
{ id: 1, name: "Scissor", price: 2, quantity: 3 },
{ id: 2, name: "Hat", price: 6.5, quantity: 1 },
{ id: 3, name: "Socks", price: 0.5, quantity: 5 },
];
// desired return
[
{ id: 1, name: "Scissor", price: 2 }
{ id: 1, name: "Scissor", price: 2 }
{ id: 1, name: "Scissor", price: 2 }
{ id: 2, name: "Hat", price: 6.5}
{ id: 3, name: "Socks", price: 0.5 }
{ id: 3, name: "Socks", price: 0.5 }
{ id: 3, name: "Socks", price: 0.5 }
{ id: 3, name: "Socks", price: 0.5 }
{ id: 3, name: "Socks", price: 0.5 }
]
My code:
const objects = [
{ id: 1, name: "Scissor", price: 2, quantity: 3 },
{ id: 2, name: "Hat", price: 6.5, quantity: 1 },
{ id: 3, name: "Socks", price: 0.5, quantity: 5 },
];
let newObjects= [];
Object.entries(objects).forEach(([key, value]) => {
for (let i=0; i < value.quantity; i++){
newObjects.push({ id: value.id, name: value.name, price: value.price})
}
});
console.log(newObjects);
So my code above does work, does return what i wanted, however i feel like there is a better/smoother and more of ES6 and beyond method. Could anyone please suggest a better way?
You could use .fill() and .flatMap().
const objects = [
{ id: 1, name: "Scissor", price: 2, quantity: 3 },
{ id: 2, name: "Hat", price: 6.5, quantity: 1 },
{ id: 3, name: "Socks", price: 0.5, quantity: 5 },
];
let newObjects = objects.flatMap(e=>
Array(e.quantity).fill({id: e.id, name: e.name, price: e.price})
);
console.log(newObjects);
You can use an array reduce along with an array fill.
The map is required only if you want to have unique references otherwise you can fill using the same object.
const objects = [
{ id: 1, name: "Scissor", price: 2, quantity: 3 },
{ id: 2, name: "Hat", price: 6.5, quantity: 1 },
{ id: 3, name: "Socks", price: 0.5, quantity: 5 },
];
const output = objects.reduce((a, c) => {
return a.concat(Array(c.quantity).fill({}).map(x=>({
id: c.id,
name: c.name,
price: c.price
})))
}, []);
console.log(output)

Problem with grouping array of objects in JS [duplicate]

This question already has answers here:
How to loop an object in React?
(8 answers)
Closed 1 year ago.
I have such array of objects:
[{id: 1, name: 'Apple', category: 'Fruit'}
{id: 2, name: 'Melon', category: 'Fruit'}
{id: 3, name: 'iPhone', category: 'Phone'}
{id: 4, name: 'Samsung Galaxy Note 8', category: 'Phone'}
{id: 5, name: 'Playstation 5', category: 'Entertainment'}]
and what I wanted to achieve is to combine product names by category and show them like:
Fruit
Apple
Melon
Phone
iPhone
Samsung Galaxy Note 8
Entertainment
Playstation 5
So, what I tried to achieve that is
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
const products = [{id: 1, name: 'Apple', category: 'Fruit'}
{id: 2, name: 'Melon', category: 'Fruit'}
{id: 3, name: 'iPhone', category: 'Phone'}
{id: 4, name: 'Samsung Galaxy Note 8', category: 'Phone'}
{id: 5, name: 'Playstation 5', category: 'Entertainment'}]
console.log(groupBy([products], 'category'));
You have to adjust your reduce a bit:
const mapping = arr.reduce((obj, entry) => {
obj[entry.category] = obj[entry.category] ? [...obj[entry.category], entry] : [entry]
return obj;
}, {})
resulting in
{
Entertainment: [{
category: "Entertainment",
id: 5,
name: "Playstation 5"
}],
Fruit: [{
category: "Fruit",
id: 1,
name: "Apple"
}, {
category: "Fruit",
id: 2,
name: "Melon"
}],
Phone: [{
category: "Phone",
id: 3,
name: "iPhone"
}, {
category: "Phone",
id: 4,
name: "Samsung Galaxy Note 8"
}]
}
And you can adjust what you want to save by changing entry to a desired value of entry.
I would approach your groupBy method like this:
function groupBy(data, key, value) {
const groups = {};
data.forEach(element => {
let subkey = element[key];
if (!(subkey in groups)) {
groups[subkey] = [element[value]];
} else {
groups[subkey].push(element[value]);
}
});
return groups;
}
console.log(groupBy(products, "category", "name")
You loop over every element, and if the specified key is not already in the groups object it will be added. if it's already added we just add the new element to the array.
This is an example return value of the groupBy function:
{
Fruit: [ 'Apple', 'Melon' ],
Phone: [ 'iPhone', 'Samsung Galaxy Note 8' ],
Entertainment: [ 'Playstation 5' ]
}
To generate HTML code
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
const products = [{id: 1, name: 'Apple', category: 'Fruit'},
{id: 2, name: 'Melon', category: 'Fruit'},
{id: 3, name: 'iPhone', category: 'Phone'},
{id: 4, name: 'Samsung Galaxy Note 8', category: 'Phone'},
{id: 5, name: 'Playstation 5', category: 'Entertainment'}];
const groups = groupBy(products, 'category');
const html = Object.keys(groups).reduce((code, cat) => {
const inner = groups[cat].reduce((i, product) => {
return i + `<p>${product.name}</p>`;
}, '');
return code + `<div><h2>${cat}</h2>${inner}</div>`;
}, '');
document.getElementById('container').innerHTML = html;
p { margin-left: 20px; }
<div id="container"></div>
var groupBy = function (xs, key) {
return xs.reduce(function (rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
const arrayData = [
{ id: 1, name: 'Apple', category: 'Fruit' },
{ id: 2, name: 'Melon', category: 'Fruit' },
{ id: 3, name: 'iPhone', category: 'Phone' },
{ id: 4, name: 'Samsung Galaxy Note 8', category: 'Phone' },
{ id: 5, name: 'Playstation 5', category: 'Entertainment' }
]
console.log(groupBy(arrayData, 'category'));
result:
{
"Fruit": [
{
"id": 1,
"name": "Apple",
"category": "Fruit"
},
{
"id": 2,
"name": "Melon",
"category": "Fruit"
}
],
"Phone": [
{
"id": 3,
"name": "iPhone",
"category": "Phone"
},
{
"id": 4,
"name": "Samsung Galaxy Note 8",
"category": "Phone"
}
],
"Entertainment": [
{
"id": 5,
"name": "Playstation 5",
"category": "Entertainment"
}
]
}

How to find a value in an array of objects using JavaScript?

I want to:
Find the cheapest and most expensive food and drink.
Find the id and name of drinks and foods if their price is higher than 10.
My attempt:
let menu = [
{ id: 1, name: "Soda",price: 3.12,size: "4oz",type: "Drink" },
{ id: 2, name: "Beer", price: 6.50, size: "8oz", type: "Drink" },
{ id: 3, name: "Margarita", price: 12.99, size: "12oz", type: "Drink" },
{ id: 4, name: "Pizza", price: 25.10, size: "60oz", type: "Food" },
{ id: 5, name: "Kebab", price: 31.48, size: "42oz", type: "Food" },
{ id: 6, name: "Berger", price: 23.83, size: "99oz", type: "Food" }
]
I would be happy and thankful if anybody could help.Thanks in Advanced.
here is an example :
let menu = [
{ id: 1, name: "Soda",price: 3.12,size: "4oz",type: "Drink" },
{ id: 2, name: "Beer", price: 6.50, size: "8oz", type: "Drink" },
{ id: 3, name: "Margarita", price: 12.99, size: "12oz", type: "Drink" },
{ id: 4, name: "Pizza", price: 25.10, size: "60oz", type: "Food" },
{ id: 5, name: "Kebab", price: 31.48, size: "42oz", type: "Food" },
{ id: 6, name: "Berger", price: 23.83, size: "99oz", type: "Food" }
];
function getCheapest(array) {
return Math.min(...array.map(item => item.price));
}
function getExpensive(array) {
return Math.max(...array.map(item => item.price));
}
function getFoodsAndDrinksMoreThan(array, minVal = 10){
return array.filter(item => item.price > minVal).map(item => ({id: item.id, name: item.name}));
}
console.log(getCheapest(menu));
console.log(getExpensive(menu));
console.log(getFoodsAndDrinksMoreThan(menu));
The first one could be achieved with Array.prototype.reduce by comparing the first and second argument of the function you pass into it and returning the smaller one.
For 2. that sounds like a case for Array.prototype.filter to me.
Let me know if you need more guidance.

How to find matches in an array with objects and change property?

When names match in object property "name" I want to change this property to "TV(1)" for the first match and increase a counter for every next match, but it's doesn't work for me (please run code to see the problem), how I can change array with objects regarding that?
const array = [
{name: "TV", price: 12323, id: 321 },
{name: "Kettle", price: 123, id: 1211 },
{name: "TV", price: 3434, id: 3434312 },
{name: "Car", price: 12343, id: 123123123 },
{name: "TV", price: 4554, id: 2313123123311 }
]
const nameIsMatch = (name, id) => {
let count = 0
return array.map((item) => {
const condition = item.name === name && item.id !== id
const formattedName = `${item.name}(${count += 1})`
return {
price: item.price,
name: condition ? formattedName : item.name,
id: item.id
}
})
}
array.forEach((el) => {
console.log(nameIsMatch(el.name, el.id))
})
The easy fix is to just rework a tiny bit of code with the old technique of moving things around to where they belong:
return array.map((item) => {
return {
price: item.price,
name: (item.name === name && item.id !== id) ? `${item.name}(${count += 1})` : item.name,
id: item.id
}
})
It's now a bit of a mess due to the ternary, so you could always unwind that a bit:
return array.map(item => {
// Make a copy of `item`
let i = { ...item };
// Override if necessary
if (item.name === name && item.id !== id) {
i. name = `${item.name}(${count += 1})`;
}
return i;
})
Where that's not as concise, but it's a lot more clear.
Use an object that keeps a counter for each name.
const array = [
{name: "TV", price: 12323, id: 321 },
{name: "Kettle", price: 123, id: 1211 },
{name: "TV", price: 3434, id: 3434312 },
{name: "Car", price: 12343, id: 123123123 },
{name: "TV", price: 4554, id: 2313123123311 }
];
const nameIsMatch = (array) => {
let counts = {};
return array.map(({
name,
price,
id
}) => {
let count = counts[name] || 1;
const formattedName = `${name}(${count})`
counts[name] = count + 1;
return {
price,
name: formattedName,
id
}
})
}
console.log(nameIsMatch(array))
You can use the following code
const array = [
{name: "TV", price: 12323, id: 321 },
{name: "Kettle", price: 123, id: 1211 },
{name: "TV", price: 3434, id: 3434312 },
{name: "Car", price: 12343, id: 123123123 },
{name: "TV", price: 4554, id: 2313123123311 }];
const hash = {};
array.forEach(function(obj){
if(!hash[obj.name])
hash[obj.name] = 1;
obj.name = `${obj.name}(${hash[obj.name]++})`;
})
console.log(array)
const array = [
{name: "TV", price: 12323, id: 321 },
{name: "Kettle", price: 123, id: 1211 },
{name: "TV", price: 3434, id: 3434312 },
{name: "Car", price: 12343, id: 123123123 },
{name: "TV", price: 4554, id: 2313123123311 }];
const result = array.map((data, index) => {
if(data.name === 'TV') {
return {...data, name:'TV'+'('+index+')'}
}
return data
})
console.info(result)
You have to use like this It's working for me as per your output
you could actually do it one by one even though this step will take a lot of time, however this will gives you a better understanding.
Data
const array = [
{name: "TV", price: 12323, id: 321 },
{name: "Kettle", price: 123, id: 1211 },
{name: "TV", price: 3434, id: 3434312 },
{name: "Car", price: 12343, id: 123123123 },
{name: "TV", price: 4554, id: 2313123123311 }
]
function to get all the Data Name. This will return [ 'TV', 'Kettle', 'Car' ]
function getAllDataName(arr){
let a = arr.map((dataName) => dataName["name"] )
let b = []
a.filter((dataName) => {
// includes() used to find the intersect of array with particular data
if(!b.includes(dataName)){
b.push(dataName)
}
})
return b
}
function to map all the Data Name accordingly
function remapDataName(arr, dataName){
let s = [...arr] //
console.log(s)
console.log(dataName)
dataName.forEach(function(val, counter = 0){
counter = 0
s.forEach(function(data){
if(data["name"] == val){
data["name"] += "(" + counter++ + ")"
}
})
})
return s
}
call those functions
let objectName = getAllDataName(array)
let objectNameMapped = remapDataName(array, objectName)
objectNameMapped will print
[
{ name: 'TV(0)', price: 12323, id: 321 },
{ name: 'Kettle(0)', price: 123, id: 1211 },
{ name: 'TV(1)', price: 3434, id: 3434312 },
{ name: 'Car(0)', price: 12343, id: 123123123 },
{ name: 'TV(2)', price: 4554, id: 2313123123311 }
]
JavaScript:
const array = [
{ name: 'TV', price: 12323, id: 321 },
{ name: 'Kettle', price: 123, id: 1211 },
{ name: 'TV', price: 3434, id: 3434312 },
{ name: 'Car', price: 12343, id: 123123123 },
{ name: 'TV', price: 4554, id: 2313123123311 },
];
const tv_array = [];
let count = 1
for (let i = 0; i < array.length; i++) {
let name = array[i].name;
if (name === 'TV') {
tv_array.push({
name: `TV(${count})`,
price: array[i].price,
id: array[i].id,
})
count++
} else {
tv_array.push(array[i]);
}
}
console.log(tv_array);
Output:
[
{ name: 'TV(1)', price: 12323, id: 321 },
{ name: 'Kettle', price: 123, id: 1211 },
{ name: 'TV(2)', price: 3434, id: 3434312 },
{ name: 'Car', price: 12343, id: 123123123 },
{ name: 'TV(3)', price: 4554, id: 2313123123311 }
]

Get element, remove duplicates and count total of each

I have the following list (object):
var data = [{
id: 1,
type: 'smartphone',
details: [{
brand: 'Samsung',
model: 'Galaxy A5'
}]
},
{
id: 2,
type: 'smartphone',
details: [{
brand: 'iPhone',
model: '6 plus'
}]
},
{
id: 3,
type: 'smartphone',
details: [{
brand: 'Samsung',
model: 'Galaxy A5'
}]
},
{
id: 4,
type: 'smartphone',
details: [{
brand: 'iPhone',
model: '7 plus'
}]
},
{
id: 5,
type: 'phone',
details: [{
brand: 'Nokia',
model: '3310'
}]
},
{
id: 6,
type: 'smartphone',
details: [{
brand: 'Samsung',
model: 'Galaxy A5'
}]
},
{
id: 7,
type: 'smartphone',
details: [{
brand: 'iPhone',
model: '6 plus'
}]
}
]
I try to show on the page a filtered list of phones and with the total amount of them using javascript/ angular
So on my page I want the following result:
Phone: Samsung, Model: Galaxy A5, Total: 3
Phone: iPhone, Model: 7 plus, Total: 1
Phone: iPhone, Model: 6 plus, Total: 2
Phone: Nokia, Model: 3310, Total: 1
I will render it using ngFor but not quite sure how to filter and count total of each model at the same time.
Thanks in advance!
You can use reduce() method to transform your data to one object or array of objects and also calculate total for each model and brand.
var data = [{"id":1,"type":"smartphone","details":[{"brand":"Samsung","model":"Galaxy A5"}]},{"id":2,"type":"smartphone","details":[{"brand":"iPhone","model":"6 plus"}]},{"id":3,"type":"smartphone","details":[{"brand":"Samsung","model":"Galaxy A5"}]},{"id":4,"type":"smartphone","details":[{"brand":"iPhone","model":"7 plus"}]},{"id":5,"type":"phone","details":[{"brand":"Nokia","model":"3310"}]},{"id":6,"type":"smartphone","details":[{"brand":"Samsung","model":"Galaxy A5"}]},{"id":7,"type":"smartphone","details":[{"brand":"iPhone","model":"6 plus"}]}]
var result = data.reduce((r, {details}) => {
details.forEach(({brand, model}) => {
let key = `${brand}|${model}`;
if(!r[key]) r[key] = {phone: brand, model, total: 0}
r[key].total++;
})
return r;
}, {})
console.log(Object.values(result))
You can use reduce to remove the duplicates..
var data = [{
id: 1,
type: 'smartphone',
details: [{
brand: 'Samsung',
model: 'Galaxy A5'
}]
},
{
id: 2,
type: 'smartphone',
details: [{
brand: 'iPhone',
model: '6 plus'
}]
},
{
id: 3,
type: 'smartphone',
details: [{
brand: 'Samsung',
model: 'Galaxy A5'
}]
},
{
id: 4,
type: 'smartphone',
details: [{
brand: 'iPhone',
model: '7 plus'
}]
},
{
id: 5,
type: 'phone',
details: [{
brand: 'Nokia',
model: '3310'
}]
},
{
id: 6,
type: 'smartphone',
details: [{
brand: 'Samsung',
model: 'Galaxy A5'
}]
},
{
id: 7,
type: 'smartphone',
details: [{
brand: 'iPhone',
model: '6 plus'
}]
}
]
var reduced = data.reduce((acc, eachElem) => {
var foundIndex = acc.findIndex((e) => {
return eachElem.type == e.type && eachElem.details[0].brand == e.details[0].brand && eachElem.details[0].model == e.details[0].model;
});
if (foundIndex === -1) {
eachElem["count"] = 1;
acc.push(eachElem);
}
else {
acc[foundIndex]['count']++
}
return acc;
}, [])
console.log(reduced);

Categories