Related
This question already has answers here:
Intersect and merge two array of objects
(2 answers)
Closed 2 years ago.
I have two arrays of equal length, each does contain object data.
This is the example code of the first array ...
const array1 = [{
key: '5',
value: '550',
}, {
key: '6',
value: '750',
}];
And here is the code for the second one ...
const array2 = [{
type: 'Job',
status: 'Finished',
key : '5',
}, {
type: 'Ticket',
status: 'Processing',
key : '6',
}];
In order to further process my data I need an intersection of both arrays with their corresponding object items being merged. The result should look like this ...
[{
key: '5',
value: '550',
type: 'Job',
status: 'Finished',
}, {
key: '6',
value: '750',
type: 'Ticket',
status: 'Processing',
}]
What I have come up with so far is ..
array1.forEach(function (element) {
array2.forEach(function (element) {
return {
type: 'Ticket',
status: 'Processing'
};
});
});
I don't know how to create the expected result. How would a solution to my problem look like?
You could store the objects in a hash table by key and merge the other array with the data from the hash table.
const
array1 = [{ key: '5', value: '550' }, { key: '6', value: '750' }],
array2 = [{ type: 'Job', status: 'Finished', key: '5' }, { type: 'Ticket', status: 'Processing', key: '6' }],
temp = Object.fromEntries(array2.map(o => [o.key, o])),
result = array1.map(o => ({ ...o, ...temp[o.key] }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It sounds like you need to combine objects in two arrays by their key.
Array methods are a good choice, but you should also look into other methods.
const combinedItemsArray = array1.map(item1 => {
const matchingItem = array2.find(item2 => item2.key === item1.key)
return {...item1, ...matchingItem }
})
This uses the spread operator to combine the values of items with matching keys.
Some things to consider:
Array.find only matches the first item in array 2 that satisfies the result. So if the arrays have items with duplicate keys, you need to modify this.
This will break down on a performance level if you have lots of items in your arrays (10s of 1000's). In that case the hash table answer might be a better approach.
its a great idea to refer to the common array methods often on mdn.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
use the sidebar to find the one that you might need!
Make use of Array.prototype.map which can be passed an additional target object to ... here one would use the second array from which one wants to retrieve the corresponding merger item.
The merging is done via Object.assign where one does merge both array items to a newly created object in order to not mutate any of the merger items of either involved array ...
const array1 = [{
key: '5',
value: '550',
}, {
key: '6',
value: '750',
}];
const array2 = [{
type: 'Job',
status: 'Finished',
key : '5',
}, {
type: 'Ticket',
status: 'Processing',
key : '6',
}];
function mergeWithSameIndexItemFromBoundArray(item, idx) {
const boundArray = this;
return Object.assign({}, item, boundArray[idx]);
}
console.log(
'merge result of array2 and array1 ...',
array2.map(mergeWithSameIndexItemFromBoundArray, array1)
);
console.log(
'merge result of array1 and array2 ...',
array1.map(mergeWithSameIndexItemFromBoundArray, array2)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
In case of arrays of non matching item orders one can build on the above approach of providing an additional target object to map.
This time it will not be the second array but a transformation of it; an object which has mapped all of this second array's items to it by using the very property name by which all the items are identified. For the OP's example this property name is key.
Thus one needs to write an additional function which does this mapping task. The next following example chooses an approach based on Array.prototype.reduce.
In addition the mapper function has to be rewritten in order to make use of the before created bound map by simply using an array item's key property instead of the array index from before ...
const array1 = [{
key: '5',
value: '550',
}, {
key: '7',
value: '320',
}, {
key: '6',
value: '750',
}];
const array2 = [{
type: 'Ticket',
status: 'Processing',
key : '6',
}, {
type: 'Job',
status: 'Finished',
key : '5',
}, {
type: 'Task',
status: 'Pending',
key : '7',
}];
function createKeyBasedItemMap(map, item) {
map[item.key] = item;
return map;
}
function mergeWithKeyBasedItemFromBoundMap(item) {
return Object.assign({}, item, this[item.key]);
}
console.log(
'map-based merge of unordered array2 and array1 items ...',
array2.map(
mergeWithKeyBasedItemFromBoundMap,
array1.reduce(createKeyBasedItemMap, {})
)
);
console.log(
'map-based merge of unordered array1 and array2 items ...',
array1.map(
mergeWithKeyBasedItemFromBoundMap,
array2.reduce(createKeyBasedItemMap, {})
)
);
console.log('\n');
console.log(
'proof of concept ... the item-map based on array1 ...',
array1.reduce(createKeyBasedItemMap, {})
);
console.log(
'proof of concept ... the item-map based on array2 ...',
array2.reduce(createKeyBasedItemMap, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
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");`
I'm new to JS and experimenting the things. I have following object:
var data = {'name': 'john', 'old': 18, 'place': 'USA'}
How can I switch the keys of this object with the following array?
var array = ['First Name', 'age', 'country']
To look following:
{'First Name': 'john', 'age': 18, 'country': 'USA'}
The only way to rename a key of an object is to assign the value of the old key to the new key and then delete the old key
Object[ new_key ] = Object[ old_key ];
delete Object[ old_key ];
Another way is to create a completely new object (with the new keys), assign the values to the new keys and then delete the old object.
var array = ['First Name', 'age', 'country'];
var data = {'name': 'john', 'old': 18, 'place': 'USA'};
var keys = Object.keys(data);
var newData = {};
for (var a in array) {
//using new obj
newData[array[a]] = data[keys[a]];
//editing same obj
data[array[a]] = data[keys[a]];
delete data[keys[a]];
}
console.log(data);
console.log(newData);
var array = ['First Name', 'age', 'country'];
var list = [
{ 'name': 'john 1', 'old': 18, 'place': 'USA' },
{ 'name': 'john 2', 'old': 19, 'place': 'USB' },
{ 'name': 'john 3', 'old': 20, 'place': 'USC' },
];
var newList = [];
for (var item in list) {
var newData = {};
for (var a in array) {
newData[array[a]] = list[item][Object.keys(list[item])[a]];
}
newList.push(newData);
}
console.log(newList);
You can use Object.assign(), Object.entries(), .map(), spread element and computed properties to assign the property name to a new object having value to to current index of property, value within iteration, set identifier for original object to result of Object.assign() call
let array = ['First Name', 'age', 'country']
let data = {'name': 'john', 'old': 18, 'place': 'USA'}
data = Object.assign({}, ...Object.entries(data)
.map(([, prop], index) => ({[array[index]]: prop})));
console.log(data);
Rather than switching the object keys; which cannot be done and you'd have to delete keys and add the new one, you could simply create a new object with the desired keys:
var data2 = {};
data2['First Name'] = data.name;
data2.age = data.old;
data2country = data.place;
You could use an object with the replacement keys and iterate it for changing the keys.
var data = { name: 'john', old: 18, place: 'USA' },
newKeys = { name: 'First Name', old: 'age', place: 'country' };
Object.keys(newKeys).forEach(function (k) {
data[newKeys[k]] = data[k];
delete data[k];
});
console.log(data);
var data = {'name': 'john', 'old': 18, 'place': 'USA'}
var ary = ['First Name', 'age', 'country']
// create key-value pairs array
var obj_entries = Object.entries(data)
var new_data =ary.reduce((acc, value, idx)=>{
acc[value]=obj_entries[idx][1];
return acc;
}, {})
console.log(new_data)
Maybe a functional approach
I'm wondering what is the cleanest way, better way to filter an array of objects depending on a string keyword. The search has to be made in any properties of the object.
When I type lea I want to go trough all the objects and all their properties to return the objects that contain lea
When I type italy I want to go trough all the objects and all their properties to return the objects that contain italy.
I know there are lot of solutions but so far I just saw some for which you need to specify the property you want to match.
ES6 and lodash are welcome!
const arrayOfObject = [{
name: 'Paul',
country: 'Canada',
}, {
name: 'Lea',
country: 'Italy',
}, {
name: 'John',
country: 'Italy',
}, ];
filterByValue(arrayOfObject, 'lea') // => [{name: 'Lea',country: 'Italy'}]
filterByValue(arrayOfObject, 'ita') // => [{name: 'Lea',country: 'Italy'}, {name: 'John',country: 'Italy'}]
You could filter it and search just for one occurence of the search string.
Methods used:
Array#filter, just for filtering an array with conditions,
Object.keys for getting all property names of the object,
Array#some for iterating the keys and exit loop if found,
String#toLowerCase for getting comparable values,
String#includes for checking two string, if one contains the other.
function filterByValue(array, string) {
return array.filter(o =>
Object.keys(o).some(k => o[k].toLowerCase().includes(string.toLowerCase())));
}
const arrayOfObject = [{ name: 'Paul', country: 'Canada', }, { name: 'Lea', country: 'Italy', }, { name: 'John', country: 'Italy' }];
console.log(filterByValue(arrayOfObject, 'lea')); // [{name: 'Lea', country: 'Italy'}]
console.log(filterByValue(arrayOfObject, 'ita')); // [{name: 'Lea', country: 'Italy'}, {name: 'John', country: 'Italy'}]
.as-console-wrapper { max-height: 100% !important; top: 0; }
Well when we already know that its not going to be a search on an object with methods, we can do the following for saving bit on time complexity :
function filterByValue(array, value) {
return array.filter((data) => JSON.stringify(data).toLowerCase().indexOf(value.toLowerCase()) !== -1);
}
Use Object.keys to loop through the properties of the object. Use reduce and filter to make the code more efficient:
const results = arrayOfObject.filter((obj)=>{
return Object.keys(obj).reduce((acc, curr)=>{
return acc || obj[curr].toLowerCase().includes(term);
}, false);
});
Where term is your search term.
You can always use array.filter() and then loop through each object and if any of the values match the value you are looking for, return that object.
const arrayOfObject = [{
name: 'Paul',
country: 'Canada',
}, {
name: 'Lea',
country: 'Italy',
}, {
name: 'John',
country: 'Italy',
}, ];
let lea = arrayOfObject.filter(function(obj){
//loop through each object
for(key in obj){
//check if object value contains value you are looking for
if(obj[key].includes('Lea')){
//add this object to the filtered array
return obj;
}
}
});
console.log(lea);
This code checks all the nested values until it finds what it's looking for, then returns true to the "array.filter" for the object it was searching inside(unless it can't find anything - returns false). When true is returned, the object is added to the array that the "array.filter" method returns. When multiple keywords are entered(spaced out by a comma and a space), the search is narrowed further, making it easier for the user to search for something.
Stackblitz example
const data = [
{
a: 'aaaaaa',
b: {
c: 'c',
d: {
e: 'e',
f: [
'g',
{
i: 'iaaaaaa',
j: {},
k: [],
},
],
},
},
},
{
a: 'a',
b: {
c: 'cccccc',
d: {
e: 'e',
f: [
'g',
{
i: 'icccccc',
j: {},
k: [],
},
],
},
},
},
{
a: 'a',
b: {
c: 'c',
d: {
e: 'eeeeee',
f: [
'g',
{
i: 'ieeeeee',
j: {},
k: [],
},
],
},
},
},
];
function filterData(data, filterValues) {
return data.filter((value) => {
return filterValues.trim().split(', ').every((filterValue) => checkValue(value, filterValue));
});
}
function checkValue(value, filterValue) {
if (typeof value === 'string') {
return value.toLowerCase().includes(filterValue.toLowerCase());
} else if (typeof value === 'object' && value !== null && Object.keys(value).length > 0) {
if (Array.isArray(value)) {
return value.some((v) => checkValue(v, filterValue));
} else {
return Object.values(value).some((v) => checkValue(v, filterValue));
}
} else {
return false;
}
}
console.log(filterData(data, 'a, c'));
console.log(filterData(data, 'a, c, ic'));
One way would be to use Array#filter, String#toLowerCase and String#indexOf like below.
const arrayOfObject = [{
name: 'Paul',
country: 'Canada',
}, {
name: 'Lea',
country: 'Italy',
}, {
name: 'John',
country: 'Italy',
}];
function filterByValue(arrayOfObject, term) {
var ans = arrayOfObject.filter(function(v,i) {
if(v.name.toLowerCase().indexOf(term) >=0 || v.country.toLowerCase().indexOf(term) >=0) {
return true;
} else false;
});
console.log( ans);
}
filterByValue(arrayOfObject, 'ita');
function filterByValue(arrayOfObject,words){
let reg = new RegExp(words,'i');
return arrayOfObject.filter((item)=>{
let flag = false;
for(prop in item){
if(reg.test(prop)){
flag = true;
}
}
return flag;
});
}
Here's how I would do it using lodash:
const filterByValue = (coll, value) =>
_.filter(coll, _.flow(
_.values,
_.partialRight(_.some, _.method('match', new RegExp(value, 'i')))
));
filterByValue(arrayOfObject, 'lea');
filterByValue(arrayOfObject, 'ita');
Here is a version of the above which filters by a value which is derived from an array of object's property. The function takes in the array of objects and the specified array of object's property key.
// fake ads list with id and img properties
const ads = [{
adImg: 'https://test.com/test.png',
adId: '1'
}, {
adImg: 'https://test.com/test.png',
adId: '2'
}, {
adImg: 'https://test.com/test.png',
adId: '3'
}, {
adImg: 'https://test.com/test-2.png',
adId: '4'
}, {
adImg: 'https://test.com/test-2.png',
adId: '5'
}, {
adImg: 'https://test.com/test-3.png',
adId: '6'
}, {
adImg: 'https://test.com/test.png',
adId: '7'
}, {
adImg: 'https://test.com/test-6.png',
adId: '1'
}];
// function takes arr of objects and object property
// convert arr of objects to arr of filter prop values
const filterUniqueItemsByProp = (arrOfObjects, objPropFilter) => {
return arrOfObjects.filter((item, i, arr) => {
return arr.map(prop => prop[objPropFilter]).indexOf(item[objPropFilter]) === i;
});
};
const filteredUniqueItemsByProp = filterUniqueItemsByProp(ads, 'adImg');
console.log(filteredUniqueItemsByProp);
I am trying to push the value of a specific key into an array based on the value of another in the same object. Is this possible?
For example:
var goodFruit = [];
var obj = [{ status: 'good', value: 'apple' },{ status: 'bad', value: 'orange' },{ status: 'good', value: 'grape' },{ status: 'bad', value: 'lemon' }];
I want to then push only the value of the status:good into the goodFruit array so the result would look like:
var goodFruit = ['apple', 'grape'];
obj.forEach(function(o){
if(o.status==='good'){
goodFruit.push(o.value)
}
})
Use filter and map:
The filter() method creates a new array with all elements that pass
the test implemented by the provided function.
The map() method creates a new array with the results of calling a
provided function on every element in this array.
var obj = [{ status: 'good', value: 'apple' },{ status:'bad', value: 'orange' },{ status: 'good', value: 'grape' },{ status: 'bad', value: 'lemon' }];
var goodFruits=obj.filter(function(a){
return a.status=='good';
}).map(function(b){
return b.value;
});
console.log(goodFruits)
Or as Suggested by Ted Hopp, You can use reduce which is more efficient with just a one pass
The reduce() method applies a function against an accumulator and each
value of the array (from left-to-right) to reduce it to a single
value.
for(var i =0;i<obj.length;i++){
if(obj[i].status==='good'){
goodFruit.push(obj[i].value)
}
}
console.log(goodFruit);
You can use reduce() for this:
var obj = [
{ status: 'good', value: 'apple' },
{ status: 'bad', value: 'orange' },
{ status: 'good', value: 'grape' },
{ status: 'bad', value: 'lemon' }
];
var goodFruit = obj.reduce(
function(result, item) {
if (item.status === 'good') {
result.push(item.value);
}
return result;
}, []
);
alert(goodFruit);