I'm trying to create an array from items inside objects, as well as items inside arrays inside objects in a vue app, by using foreach to loop over them. It works well when I only have one single item, but I can't figure out how to loop over an array inside the object and add all of those items to the array I'm creating.
What I have now
const examples = [
{
name: "Example 1",
type: ["Meat", "Water", "Dairy"],
showDetail: false
},
{
name: "Example 2",
type: Fruit,
showDetail: false
},
{
name: "Example 3",
type: Vegetable,
showDetail: false
}
]
new Vue({
data: {
examplesList: examples,
type: ''
},
methods: {
filterList: function() {
this.type = event.target.value;
}
},
computed: {
uniqueList: function() {
const types = [];
this.examplesList.forEach((example) => {
if (!types.includes(example.type)) {
types.push(example.type);
}
});
return types;
}
}
})
It works fine if I remove the object with the array inside of "type", and adds the Fruit and Vegetable items to the array. Any ideas?
Desired output:
["Meat", "Water", "Dairy", "Fruit", "Vegetable"]
CodePudding user response:
Here is one possible solution. You'll need to translate the solution to vue, of course, but the problem here really doesn't have anything to do with vue specifically so I've shown a vanilla javascript solution just to keep things simple.
const examples = [
{
name: "Example 1",
type: ["Meat", "Water", "Dairy", "Fruit"],
showDetail: false
},
{
name: "Example 2",
type: "Fruit",
showDetail: false
},
{
name: "Example 3",
type: "Vegetable",
showDetail: false
}
];
const types = [];
examples.forEach((example) => {
const exampleTypes = Array.isArray(example.type)
? example.type
: [example.type];
for (let exampleType of exampleTypes) {
if (!types.includes(exampleType)) {
types.push(exampleType);
}
}
});
console.log(types);
CodePudding user response:
Here's an abstract way of doing that using a Set. Sets guarantee unique values meaning there's no need to check if an item is present or not.
Using just an array will become increasingly expensive to check if an item was already added as it will have to scan the entire array for each includes, O(n) time complexity.
const examples = [{
name: "Example 1",
type: ["Meat", "Water", "Dairy"],
showDetail: false
},
{
name: "Example 2",
type: "Fruit",
showDetail: false
},
{
name: "Example 3",
type: "Vegetable",
showDetail: false
}
];
const typeSet = new Set();
let types;
examples.forEach((example) => {
if (Array.isArray(example.type)) {
example.type.forEach(type => {
typeSet.add(type);
});
} else {
typeSet.add(example.type);
}
});
types = [...typeSet];
console.log(types);
CodePudding user response:
Here is one possible solution to achieve the desired result:
computed: {
uniqueList: function() {
return this.examplesList.reduce(
(acc, itm) => (
Array.isArray(itm.type)
? itm.type.filter(t => !acc.includes(t)).length > 0
? [
...acc,
...itm.type.filter(t => !acc.includes(t))
]
: acc
: acc.includes(itm.type)
? acc
: [...acc, itm.type]
), []
)
}
}
Explanation
reduceis used on the arraythis.examplesList- Each item
itmis processed andaccis the accumulator/aggregator (initially set to an empty array[]) - if
itm.typeis an Array, then
- if any elements in
itm.typearray is not already present inaccarray, include it (by using the...spread operator)
- otherwise (ie,
itm.typeis a string)
- if it is not already in
acc, then include it (again, using...spread operator)
That's it !
Please comment if any further clarification/s or question/s.
Code snippet
const examples = [{
name: "Example 1",
type: ["Meat", "Water", "Dairy"],
showDetail: false
},
{
name: "Example 2",
type: "Fruit",
showDetail: false
},
{
name: "Example 3",
type: "Vegetable",
showDetail: false
}
];
const getUniqueTypes = (arr = examples) => (
arr.reduce(
(acc, itm) => (
Array.isArray(itm.type)
? itm.type.filter(t => !acc.includes(t)).length > 0
? [
...acc,
...itm.type.filter(t => !acc.includes(t))
]
: acc
: acc.includes(itm.type)
? acc
: [...acc, itm.type]
), []
)
);
console.log(getUniqueTypes());
