i want to convert array of objects into the unique array from properties
Thanks!
Income data
const food = [
{dinner: 'Apple', breakfest: 'Tomato'},
{dinner: 'Apple', breakfest: 'Apple'}
{dinner: 'Milk', breakfest: 'Banana'}
{dinner: 'Apple', breakfest: 'Milk'}
{dinner: 'Tomato', breakfest: 'Banana'}
]
Receive
const whatIAmEat = ['Apple', 'Tomato', 'Milk', 'Banana']
I know i can receive it with ...new Set than concat() in separate variables, but it looks to complicated.
Maybe create an empty array (here: result), and then loop over food, adding dinner/breakfast values into result if they're not already included.
const food=[{dinner:"Apple",breakfast:"Tomato"},{dinner:"Apple",breakfast:"Apple"},{dinner:"Milk",breakfast:"Banana"},{dinner:"Apple",breakfast:"Milk"},{dinner:"Tomato",breakfast:"Banana"}];
const result = [];
for (const { dinner, breakfast } of food) {
if (!result.includes(dinner)) result.push(dinner);
if (!result.includes(breakfast)) result.push(breakfast);
}
console.log(result);
Additional documentation
Destructuring assignment
Alternative
const food = [
{dinner: 'Apple', breakfast: 'Tomato'},
{dinner: 'Apple', breakfast: 'Apple'},
{dinner: 'Milk', breakfast: 'Banana'},
{dinner: 'Apple', breakfast: 'Milk'},
{dinner: 'Tomato', breakfast: 'Banana'}
]
const unique = {};
food.forEach(({dinner,breakfast}) => { unique[dinner] = true; unique[breakfast] = true; })
console.log(Object.keys(unique))
Set is still simpler. No need for concat
const food = [
{dinner: 'Apple', breakfast: 'Tomato'},
{dinner: 'Apple', breakfast: 'Apple'},
{dinner: 'Milk', breakfast: 'Banana'},
{dinner: 'Apple', breakfast: 'Milk'},
{dinner: 'Tomato', breakfast: 'Banana'}
]
const unique = new Set();
food.forEach(({dinner,breakfast}) => { unique.add(dinner); unique.add(breakfast) })
console.log([...unique])
👋
I am sure there are a lot of variations on how to achieve the similar thing.
One of this ways is to traverse the array, and recreate it with groups of nested arrays, discarding the need for the object entirely, flatten it and then remove duplicates with new Set(arr)
// Data
const food = [{
dinner: 'Apple',
breakfest: 'Tomato'
},
{
dinner: 'Apple',
breakfest: 'Apple'
}, {
dinner: 'Milk',
breakfest: 'Banana'
}, {
dinner: 'Apple',
breakfest: 'Milk'
}, {
dinner: 'Tomato',
breakfest: 'Banana'
}
]
// Implementation
function flattenUnique(arr) {
return [...new Set(arr.reduce((acc, elem) => [...acc, Object.values(elem)], []).flat())]
}
// Output
console.log(flattenUnique(food))
Hope it helps!
Related
This question already has answers here:
Filter array of objects based on another array in javascript
(10 answers)
Closed 3 months ago.
I am a newbie to JavaScript. I have now a nested Object:
const fruitList = [
{ fruit: {id: '1-1', fruit_name: 'Apple'},
location: {id: '2-1', location_name: 'USA'}
},
{
fruit: {id: '1-2', fruit_name: 'Banana'},
location: {id: '2-2', location_name: 'UK'}
},
{
fruit: {id: '1-3', fruit_name: 'Orange'},
location: {id: '2-1', location_name: 'USA'}
}
];
and a string array:
let keywords = ['Apple', 'Banana'];
I am trying to filter the nested Object based on the above string array and the expected outpust is :
output =[
{ fruit: {id: '1-1', fruit_name: 'Apple'},
location: {id: '2-1', location_name: 'USA'}
},
{
fruit: {id: '1-2', fruit_name: 'Banana'},
location: {id: '2-2', location_name: 'UK'}
}
];
I already tried:
const filteredFruit = fruitList.filter(({item})=>
item.fruit?.fruit_name.every(ele => keywords.includes(ele))
)
but it didn't work. I also checked all the similar questions on the Stackoverflow, but still could not find a way to solve it. Thank you very much for your help!
You were close.
item isn't a property of the object being iterated on so you can't destructure it.
You need to swap around your condition to check to see if the keywords array includes the fruit name.
const fruitList=[{fruit:{id:"1-1",fruit_name:"Apple"},location:{id:"2-1",location_name:"USA"}},{fruit:{id:"1-2",fruit_name:"Banana"},location:{id:"2-2",location_name:"UK"}},{fruit:{id:"1-3",fruit_name:"Orange"},location:{id:"2-1",location_name:"USA"}}];
const keywords = ['Apple', 'Banana'];
const filteredFruit = fruitList.filter(item =>
keywords.includes(item.fruit.fruit_name)
);
console.log(filteredFruit);
If I know an object exists in an array with a unique key:value pair do I have use .find() to get the object or is there a way that doesn't require iteration?
Given:
const testObj = [
{id: '001', first: 'fThing1', other: [{id: '001.1'}, {id: '001.2'}], arr: ['a1', 'b1', 'c1'] },
{id: '002', first: 'fThing2', other: [{id: '002.1'}, {id: '002.2'}], arr: ['a2', 'b2', 'c2'] },
{id: '003', first: 'fThing3', other: [{id: '003.1'}, {id: '003.2'}], arr: ['a3', 'b3', 'c3'] }
]
Is there a notation to do:
testObj.id['001'](some notation)first = 'something'
Or do I have to do:
temp = testObj.find(to => to.id === '001')
temp.first = 'something'
To directly answer your question...
Is there a notation to do
The answer is "no".
If your elements have unique IDs, considering collecting them into a Map keyed by id if you need that sort of access...
const testObj = [{"id":"001","first":"fThing1","other":[{"id":"001.1"},{"id":"001.2"}],"arr":["a1","b1","c1"]},{"id":"002","first":"fThing2","other":[{"id":"002.1"},{"id":"002.2"}],"arr":["a2","b2","c2"]},{"id":"003","first":"fThing3","other":[{"id":"003.1"},{"id":"003.2"}],"arr":["a3","b3","c3"]}]
const idMap = new Map(testObj.map(o => [o.id, o]))
// word of warning, this will error if the ID doesn't exist
idMap.get("001").first = "something"
console.log(testObj[0])
.as-console-wrapper { max-height: 100% !important; }
Because the object references in testObj and the Map are the same, any changes to one will be reflected in the other.
As #Phil mentioned, the notation you asked about is not possible.
Another option is to use function .map() to return a new array with updated objects:
const testObj = [
{id: '001', first: 'fThing1', other: [{id: '001.1'}, {id: '001.2'}], arr: ['a1', 'b1', 'c1'] },
{id: '002', first: 'fThing2', other: [{id: '002.1'}, {id: '002.2'}], arr: ['a2', 'b2', 'c2'] },
{id: '003', first: 'fThing3', other: [{id: '003.1'}, {id: '003.2'}], arr: ['a3', 'b3', 'c3'] }
];
const result = testObj.map(item =>
item.id === '001' ? {
...item,
first: 'something'
} : item
);
console.log(result);
I have an array of products and I'm looking to create a new array if the product title or product tags list contains a search term.
Below is a simplified version.
const products = [{title: 'apple-green', tags: [{colour: 'red', tagType: 'colour'}]},
{title: 'orange', tags: [{colour: 'orange', tagType: 'colour'}]},
{title: 'cherry', tags: [{colour: 'red', tagType: 'colour'}]},
{title: 'pear', tags: [{colour: 'green', tagType: 'colour'}]}]
const searchTerm = "green"
const result = [{title: 'apple-green', tags: [{colour: 'red', tagType: 'colour'},
{title: 'pear', tags: [{colour: 'green', tagType: 'colour'}]}]
I thinking that using reduce would be the best way to achieve this.
I have tried the below without success.
const result = products.reduce((acc, product) => {
if (product.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
product.tags.map((tag) =>
tag.title.toLowerCase().includes(searchTerm.toLowerCase())))
return product
return [acc, ...product]
}, [])
Any help would be greatly received.
Array#filter is probably the correct function for this rather than Array#reduce, since no transformation appears to be applied to the output structure (and even if it were, map/filter may be clearer than reduce).
After writing a helper function to perform a case-insensitive substring/includes check, you can use Array#some on product.tags to determine if any of the tag.colour properties from a product's tags array match the search term.
const products = [{title: 'apple-green', tags: [{colour: 'red', tagType: 'colour'}]}, {title: 'orange', tags: [{colour: 'orange', tagType: 'colour'}]}, {title: 'cherry', tags: [{colour: 'red', tagType: 'colour'}]}, {title: 'pear', tags: [{colour: 'green', tagType: 'colour'}]}];
const includesAnyCase = (s, t) => s.toLowerCase().includes(t.toLowerCase());
const filterProducts = (products, searchTerm) =>
products.filter(product =>
includesAnyCase(product.title, searchTerm) ||
product.tags.some(tag => includesAnyCase(tag.colour, searchTerm))
)
;
console.log(filterProducts(products, "green"));
you can use :
result = products.filter(product => {
return product.title.toLowerCase()includes.(searchTerm.toLowerCase())
|| product.tags.includes({colour: searchTerm.toLowerCase(), tagType: 'colour'})
}
Using reduce twice for brevity, though filter seems a more natural fit:
const products = [
{ title: 'apple-green', tags: [{ colour: 'green', tagType: 'colour' }] },
{ title: 'apple-red', tags: [{ colour: 'red', tagType: 'colour' }] },
{ title: 'orange', tags: [{ colour: 'orange', tagType: 'colour' }] },
{ title: 'cherry', tags: [{ colour: 'red', tagType: 'colour' }] },
{ title: 'pear', tags: [{ colour: 'green', tagType: 'colour' }] }
];
const searchTerm = "green";
const results = products.reduce((matchingProducts, currentProduct) => {
const matchingColoured = currentProduct.tags.reduce((p, c) => {
return c.colour === searchTerm ? [c, ...p] : null;
}, []);
return matchingColoured ? [currentProduct, ...matchingProducts] : matchingProducts;
}, []);
console.log(JSON.stringify(results, {}, 4));
Outputs:
[
{
"title": "pear",
"tags": [
{
"colour": "green",
"tagType": "colour"
}
]
},
{
"title": "apple-green",
"tags": [
{
"colour": "green",
"tagType": "colour"
}
]
}
]```
You can use the Array.prototype.filter() function to accept only the products with titles that contain the search term or with tags that === the searchTerm. Of course you can use .includes on the tags as well if that is what you wish.
JSFiddle: https://jsfiddle.net/DariusM/y1u4cedk/1/
const products = [
{title: 'apple-green', tags: [{colour: 'red', tagType: 'colour'}]},
{title: 'orange', tags: [{colour: 'orange', tagType: 'colour'}]},
{title: 'cherry', tags: [{colour: 'red', tagType: 'colour'}]},
{title: 'pear', tags: [{colour: 'green', tagType: 'colour'}]}
];
const searchTerm = "green"
const result = products.filter((product) => {
if(product.title.toLowerCase().includes(searchTerm.toLowerCase())){
return true;
}
for(i = 0; i < product.tags.length; i++){
if(product.tags[i][product.tags[i].tagType] === searchTerm){
return true;
}
}
});
console.log(result);
list1 = [{name: 'apple'}, {name: 'pear'}, {name: 'banana'}]
list2 = ['banana', 'apple', 'pear']
expectation
list1 = [{name: 'banana'}, {name: 'apple'}, {name: 'pear'}]
How to make list2 and sort list1 through angularjs ng-repeat or JS
And a map solution
var list1=[{
name: 'apple'
}, {
name: 'pear'
}, {
name: 'banana'
}];
var list2 = ['banana', 'apple', 'pear'];
list1 = list2.map(x=>{
return {name:x};
});
console.log(list1);
Based on what you have and what you wannt to have . Here is how you get.
list1 = [{name: 'apple'}, {name: 'pear'},{name: 'banana'}];
list2 = ['banana', 'apple', 'pear'];
var result = [];
function getData(){
list2.forEach(function(e){
let obj=list1.find(element=>element['name'] === e);
result.push(obj);
})
console.log(result);
}
getData();
// In case you want to preserve with list1
list1.sort(function(a, b){
return list2.indexOf(a.name) - list2.indexOf(b.name);
});
console.log(list1)
Now you can use result as inside ng-repeat ;
You can use the index of the array list2 to get the numeric values for sorting:
var list1 = [{
name: 'apple'
}, {
name: 'pear'
}, {
name: 'banana'
}]
var list2 = ['banana', 'apple', 'pear'];
list1.sort(function(a, b) {
return list2.indexOf(a.name) - list2.indexOf(b.name);
});
console.log(list1);
Try with this:
$scope.props = [{name: 'apple'}, {name: 'pear'}, {name: 'banana'}]
ng-repeat="prop in props | orderBy:'name'"
Use a compare function as below :
var list1 = [{name: 'apple'}, {name: 'pear'}, {name: 'banana'}];
var list2 = ['banana', 'apple', 'pear'];
function comp(a, b) {
return list2.indexOf(a.name) - list2.indexOf(b.name);
}
list1.sort(comp);
console.log(list1);
I'm trying to sort objects based by its property (price)
var arr = [{
name: 'Apple',
price: '1.03'
},
{
name: 'Cherry',
price: '0.33'
},
{
name: 'Mango',
price: '0.53'
}
]
Now i use lodash to sort it by its price:
arr = _.sortBy(arr, 'price' ).reverse();
Now arr[0] should be Apple, since it's price is highest, but it's not.
What could be wrong?
As everyone is mentioning you are sorting Strings so your outcome is not what you are expecting. To sort based on price use the following (or similar):
var arr = [{
name: 'Apple',
price: '1.03'
},
{
name: 'Cherry',
price: '0.33'
},
{
name: 'Mango',
price: '0.53'
}
]
arr.sort(function(a, b){
return parseFloat(b.price) - parseFloat(a.price);
});
console.log(arr);
Since there isn't an answer using Lodash here, I am going to provide one in case someone else stumbles across this question in the future:
// Your code:
var arr = [{
name: 'Apple',
price: '1.03'
},
{
name: 'Cherry',
price: '0.33'
},
{
name: 'Mango',
price: '0.53'
}
]
// Yours that isn't working:
// arr = _.sortBy(arr, 'price' ).reverse();
const sortedArr = _.chain(arr)
.map((val) => _.toNumber(val.price))
.sortBy()
.value();
console.log(sortedArr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
Since the price value inside each object is a string, you are getting a different result. You can take advantage of function callback and convert your price value to a number and the use sortBy to sort your data based on the numeric value of price.
var arr = [{name: 'Apple',price: '1.03'},{name: 'Cherry',price: '0.33'},{name: 'Mango',price: '0.53'}];
_.sortBy(arr, ({price}) => +price);
console.log(arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
If you are using LoDash, this is how i did it:
var arr = [{name: 'Apple',price: '1.03',weight:'12'},{name: 'Cherry',price: '0.33',
weight:'12',weight:'12'},{name: 'Mango',price: '0.53',weight:'12'}];
SortyBy(sortName, SortType, order)
{
if(SortType == 1)
{
_.orderBy(arr,[function(o) { return parseFloat(o[sortName]); }],order);
}
else
{
_.orderBy(arr,[function(o) { return o[sortName]; }], order);
}
}
//you would then call:
SortBy("price",1, "asc");
SortBy("weight",1, "asc");
SortBy("name",0,"desc");`