I have two arrays
array1 = [{"id":1,"name":"Michale Sharma","gender":"Male","age":25,"salary":10000},{"id":2,"name":"Sunil Das","gender":"Male","age":24,"salary":5000},{"id":3,"name":"Robin Pandey","gender":"Male","age":35,"salary":45000},{"id":4,"name":"Mona Singh","gender":"Female","age":27,"salary":12000}]
array2 = [{"Deptid":1,"Deptname":"IT"},{"Deptid":12,"Deptname":"HR"},{"Deptid":3,"Deptname":"HW"}]
Output:
{ "0": { "id": 1, "name": "Michale Sharma", "gender": "Male", "age": 25, "salary": 10000, "Deptid": 1, "Deptname": "IT" }, "1": { "id": 2, "name": "Sunil Das", "gender": "Male", "age": 24, "salary": 5000}, "2": { "id": 3, "name": "Robin Pandey", "gender": "Male", "age": 35, "salary": 45000, "Deptid": 3, "Deptname": "HW" }, "3": { "id": 4, "name": "Mona Singh", "gender": "Female", "age": 27, "salary": 12000 }, "4" :{ "Deptid": 12, "Deptname": "HR" } }
I want to merge them based on the property values e.g id of array1 and Deptid of array2
Which means if id value = 1 and Deptid value = 1, then merge the records, if not then keep the values from one array and the other will be null. In another words, kind of the functionality of FULL OUTER JOIN. Because the array data cannot be a sequential one and may not be of the same length.
I have tried with Jquery.extend as under
$.extend(true, array1,array2)
It does not accept any property but merges the arrays.
I have also seen this but it does not help.
Looks like you need to have a custom logic. Here is sample using lodash library:
var array1 = [{"id":1,"name":"Michale Sharma","gender":"Male","age":25,"salary":10000},{"id":2,"name":"Sunil Das","gender":"Male","age":24,"salary":5000},{"id":3,"name":"Robin Pandey","gender":"Male","age":35,"salary":45000},{"id":4,"name":"Mona Singh","gender":"Female","age":27,"salary":12000}]
var array2 = [{"Deptid":1,"Deptname":"IT"},{"Deptid":12,"Deptname":"HR"},{"Deptid":3,"Deptname":"HW"}]
var defaults = {
Deptid: null,
DeptName: null
};
_.each(array1, function (obj) {
var dept = _.find(array2, { Deptid: obj.id });
_.defaults(obj, dept, defaults);
});
Using LoDash, roughly something like this:
_.reduce(array1, function(res, item1) {
var found = _.find(array2, function(item2) {
return item1.id === item2.Deptid;
});
if (found) res.push(_.merge({}, item1, found));
return res;
}, []);
EDIT: Sorry, I forgot about merging empty props.
It would be something like this (unoptimized):
function join(array1, array2) {
var keys = _.keys(array2[0]);
var vals = _.map(keys, function() { return '' });
return _.map(array1, function(item1) {
var found = _.find(array2, function(item2) {
return item1.id === item2.Deptid;
});
if (found)
return _.merge({}, item1, found);
return _.merge({}, item1, _.zipObject(keys, vals));
}, []);
}
Related
I would need to check if the objects in the "food" array are equal to each other, and the ones that are - combine into one adding amount and mody.amount and leaving the rest unchanged. This is just for better order data displaying. I tried with lodash library and reduce but but I don't know exactly how to construct this function that nested objects(mody) adds values as well, and in case isEqual returns false keep the object while concurrently concatenating the identical ones.
What I tried:
obj1.reduce((prev, next) => _.isEqual(prev, next) ? {...prev, amount: prev.amount + next.amount} : next
Now it looks like that:
const food = [
{
"id": 1,
"name": "chicken",
"price": 6,
"amount": 1,
"mody": [
{
"id": 33,
"name": "cheese",
"price": 1,
"amount": 1
},
{
"id": 34,
"name": "chips",
"price": 2,
"amount": 1
}
]
},
{
"id": 1,
"name": "chicken",
"price": 6,
"amount": 1,
"mody": [
{
"id": 33,
"name": "cheese",
"price": 1,
"amount": 1
},
{
"id": 34,
"name": "chips",
"price": 2,
"amount": 1
}
]
},
{
"id": 2,
"name": "pizza",
"price": 6,
"amount": 2,
"mody": [
{
"id": 12,
"name": "extra cheese",
"price": 2,
"amount": 1
}
]
}
]
and would need something like that:
const food = [
{
"id": 1,
"name": "chicken",
"price": 6,
"amount": 2,
"mody": [
{
"id": 33,
"name": "cheese",
"price": 1,
"amount": 2
},
{
"id": 34,
"name": "chips",
"price": 2,
"amount": 2
}
]
},
{
"id": 2,
"name": "pizza",
"price": 6,
"amount": 2,
"mody": [
{
"id": 12,
"name": "extra cheese",
"price": 2,
"amount": 1
}
]
}
]
The algorithm to sum the food amounts and mody amounts are almost the same, the difference is that for each food that has the same id we will sum the mody amount. To make the algorithm simpler I used a dictionary as the accumulator on the reduce function so we have a unique element per key. This element will be our final food or mody with the amount sum.
Mody sum algoritm:
const sumMody = (modyAccumulator, currentMody) => {
//Get the stored mody in the accumulator dictionary or null if it the mody is not stored
const storedMody = modyAccumulator[currentMody.id] ?? null
// if mody is null then add mody to the dictionary using its id as key
if (!storedMody) {
modyAccumulator[currentMody.id] = currentMody
} else {
//Mody is stored then sum amount
storedMody.amount += currentMody.amount
}
return modyAccumulator
}
The food sum algoritm is the same as the sumMody, the only difference is that it calls the sumMody function when the foods are equal:
const sumFood = (foodAccumulator, currentFood) => {
//Get the stored foodin the accumulator dictionary or null if it the food is not stored
const storedFood = foodAccumulator[currentFood.id] ?? null
// if food is null then add food to the dictionary using its id as key
if (!storedFood) {
foodAccumulator[currentFood.id] = currentFood
} else {
//Food is stored then sum food amount
storedFood.amount += currentFood.amount
//Create a list with mody from both foods
const modyList = [...storedFood.mody, ...currentFood.mody]
//Use reduce passing the sumMody callback function and initialize the accumulator with a dictionary
const modySumDictionary = modyList.reduce(sumMody, {})
/* The function above return a dictionary where the identifier is the mody.id
and the value is the mody. We only need the values from that dictionary so
we use Object.values to extract all the values. */
storedFood.mody = Object.values(modySumDictionary)
}
return foodAccumulator
}
To execute both sums:
//As explained before the reduce function will return a dictionary so we use Object.values to get only the values
const result = Object.values(food.reduce(sumFood, {}))
console.log(result)
Algoritm without comments:
const sumMody = (modyAccumulator, currentMody) => {
const storedMody = modyAccumulator[currentMody.id] ?? null
if (!storedMody) {
modyAccumulator[currentMody.id] = currentMody
} else {
storedMody.amount += currentMody.amount
}
return modyAccumulator
}
const sumFood = (foodAccumulator, currentFood) => {
const storedFood = foodAccumulator[currentFood.id] ?? null
if (!storedFood) {
foodAccumulator[currentFood.id] = currentFood
} else {
storedFood.amount += currentFood.amount
const modyList = [...storedFood.mody, ...currentFood.mody]
const modySumDictionary = modyList.reduce(sumMody, {})
storedFood.mody = Object.values(modySumDictionary)
}
return foodAccumulator
}
const result = Object.values(food.reduce(sumFood, {}))
console.log(result)
Reference to Object.values
I have the following array of objects. How can I reshape the data to get the Expected Output?
let performanceReview = [
{ "name": "Sean", "Manager": "Joe", "Performance": 5},
{ "name": "John", "Manager": "Joe", "Performance": 9},
{ "name": "Paul", "Manager": "Joe", "Performance": 0},
{ "name": "Derek", "Manager": "Greg", "Performance": 10},
{ "name": "Lisa", "Manager": "Greg", "Performance": 10},
{ "name": "Julia", "Manager": "Donna", "Performance": 7}];
Expected Output
var Series = [
{Manager: "Joe", data: [["Sean", 5], ["John", 9], ["Paul", 0]]},
{Manager: "Greg", data: [["Derek", 10],["Lisa", 10]]},
{Manager: "Donna", data: [["Julia", 7]]}];
Could someone also please help walk me through their problem solving approach.
This solution breaks the task into two steps. (The code could be shorter, but this may be a bit easier to understand than trying to squeeze all the logic into a one-liner.)
Using Array.prototype.reduce, we can make an object where each property has:
a manager's name as a key and
the two-dimensional array of minions-with-ratings as the value
Then, using the for...in syntax, we can convert each property to an independent object and push it to the output array.
See the in-code comments for clarifications.
// Main
const
originalArray = getOriginalArray(),
obj = groupByManager(originalArray),
output = formatFinalArray(obj);
console.log(output);
// We make an object because, unlike Arrays, they use named properties
function groupByManager(input){
const obj = input.reduce(
// 1st argument to `reduce` is a 'reducer' function (taking two args)
// that will be applied to each item of the array
(grouped, item) => {
// 1st arg to reducer func is the accumulated value, returned after each loop
// 2nd arg to reducer func is the item of the array for the current loop
// If there's no prop named for this manager, makes one w/ empty array as value
grouped[item.Manager] = grouped[item.Manager] || [];
// Makes a two-item array of name and performance and pushes it to prop array
grouped[item.Manager].push([item.name, item.Performance]);
// The accumulated object has been updated, is ready to be used in next loop
return grouped;
},
// 2nd arg to `reduce` (an empty obj) to be used as `grouped` during first loop
{}
);
return obj;
}
// We'll pass the object into this function to get our desired format
function formatFinalArray(obj){
const output = [];
for(let key in obj){
output.push({ Manager: key, data: obj[key] });
}
return output;
}
// Just provides our original array
function getOriginalArray(){
return [
{ "name": "Sean", "Manager": "Joe", "Performance": 5 },
{ "name": "John", "Manager": "Joe", "Performance": 9 },
{ "name": "Paul", "Manager": "Joe", "Performance": 0 },
{ "name": "Derek", "Manager": "Greg", "Performance": 10 },
{ "name": "Lisa", "Manager": "Greg", "Performance": 10 },
{ "name": "Julia", "Manager": "Donna", "Performance": 7 }
];
}
I have this object structure:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21"
}
}
I'd like to change it so it appears like this:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"suppliers":[
{
"supplier_id": 1,
"supplier_name": [
"Supplier1"
]
}
],
"supplier_code": "SUP001",
"count": "21"
}
}
I tried this hoping it would work:
const group = accumulator[item.id];
group.suppliers = [];
group.suppliers = group.suppliers.push(item.supplier_name, item.supplier_id, item.supplier_code);
return accumulator;
Unfortunately that just seems to give me a count of the objects pushed into suppliers, suppliers isn't an array and supplier_id, supplier_name and supplier_code are still visible outside of suppliers:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21",
"suppliers: 3
}
}
How do I change it to the format I want?
You could use es6 Destructuring assignment, Object.values es2017 (or Object.keys instead).
If you assume that users contains more then one user you could use reduce.
In the example below original object won't be mutated.
Hope it helps
const original = {
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21"
}
}
};
const { users } = original;
const reshaped = Object.values(users).reduce((acc, { id, supplier_id, supplier_name, ...rest }) => {
acc[id] = {
...rest,
suppliers: [{
supplier_id,
supplier_name: [supplier_name]
}]
};
return acc;
}, {});
console.log(reshaped);
You need to use an object to push into the suppliers array. Also, delete the old keys which are not needed.
Edit - You can directly create an array of 1 object. Thanks #Adam
const group = accumulator[item.id];
group.suppliers = [{
supplier_id: item.supplier_id,
supplier_name: item.supplier_name,
supplier_code: item.supplier_code
}];
delete group.supplier_id;
delete group.supplier_name;
delete group.supplier_code;
return accumulator;
Here's a quick and modern solution:
const parseUsers = (users) => {
let parsedUsers = {};
for (key in users) {
const user = users[key];
// destructuring (or extracting) the relevant keys from the . user object, keeping everything else under 'rest'
const { supplier_id, supplier_name, ...rest } = user;
parsedUsers[key] = {
...rest, // spreading back our rest params
suppliers: [ // creating a new array and populating it with the keys which we previously extracted (along with their corresponding values)
supplier_id,
supplier_name
]
}
}
return parsedUsers;
}
usage: parseUsers(json.users)
I want to sort some Object look likes this
data = {
"imH3i4igFNxM3GL": {
"name": "Nacky",
"age": 12
},
"vuzPuZUmyT8Z5nE": {
"name": "Emmy",
"age": 20
},
"OkIPDY1nGjxlq3W": {
"name": "Nat",
"age": 20
}
}
I want to sort it by "name".
I tried to use Lodash for this problem.
_.sortBy(data, [function(o) { return o.name; }]);
but, it return me an array of objects without the keys
[
{
"name": "Emmy",
"age": 20
},
{
"name": "Nacky",
"age": 12
},
{
"name": "Nat",
"age": 20
}
]
I want it return me sorted object with key like the same
{
"vuzPuZUmyT8Z5nE": {
"name": "Emmy",
"age": 20
},
"imH3i4igFNxM3GL": {
"name": "Nacky",
"age": 12
},
"OkIPDY1nGjxlq3W": {
"name": "Nat",
"age": 20
}
}
what should I do? thanks
Objects in JS can't be sorted, and the order of the properties is not reliable, ie it depends on browsers' implementations. That's why _.sortBy() is converting your object into a sorted array.
I can think of 2 options to work with that.
Add the key to the objects in the array
If you just need an ordered array with the keys in the objects, so you can render a list.
var data = {
"imH3i4igFNxM3GL": {
"name": "Nacky",
"age": 12
},
"vuzPuZUmyT8Z5nE": {
"name": "Emmy",
"age": 20
},
"OkIPDY1nGjxlq3W": {
"name": "Nat",
"age": 20
}
};
var result = _(data)
.map(function(v, k) { // insert the key into the object
return _.merge({}, v, { key: k });
})
.sortBy('name') // sort by name
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Create an order array
Create an array of ordered keys, and use them when you wish to render the objects in order.
var data = {
"imH3i4igFNxM3GL": {
"name": "Nacky",
"age": 12
},
"vuzPuZUmyT8Z5nE": {
"name": "Emmy",
"age": 20
},
"OkIPDY1nGjxlq3W": {
"name": "Nat",
"age": 20
}
};
var orderArray = _(data)
.keys() // create an array of keys
.sortBy(function(key) { // sort the array using the original names
return data[key].name;
}) // sort by name
.value();
console.log('The order array', orderArray);
console.log(orderArray.map(function(k) {
return data[k];
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
I use something like this.
let data = {
'g34ghgj3kj': {
YOUR_KEY: 'g34ghgj3kj',
'key1': false,
'key2': false,
},
'hh334h664': {
YOUR_KEY: 'hh334h664',
'key1': true,
'key2': false,
},
//{...}
};
_.orderBy(data, ['key1', 'key2'], ['desc', 'desc']).reduce((result, value) => {
result[value.YOUR_KEY] = value;
return result;
}, {});
Here is the test array:
var test = [
{
"id": 1,
"team": [
{
"name": "Alex",
"age": 27,
"checked": true
},
{
"name": "Bob",
"age": 35,
"checked": false
}
],
"checked": true
},
{
"id": "2",
"team": [
{
"name": "Jack",
"age": 37,
"checked": false
},
{
"name": "Tom",
"age": 29,
"checked": true
}
],
"checked": true
}];
And the result that I want to get is an array like this:
result = ["Alex", "Tom"];
The result array contains all items that the "checked" attribute equals to true in team.
I try to get the result with the underscore.js filter, but I cannot get the correct result.
If you have a better solution, please tell me.
Here is my code:
_.filter(test, function(team) {
_.filter(team, function(worker){
if(worker.checked)
return worker.name;
});});
Here's how you can do it in both underscore and lodash:
Underscore jsfiddle:
var result = _.chain(test).pluck('team').flatten().filter({checked:true}).value();
Lodash jsfiddle:
var result = _(test).pluck('team').flatten().filter({checked:true}).value();
Take the team arrays together, flatten them so you have the nested arrays, filter on the property and the result are the objects containing the name and checked being true. If you want just the names, do another pluck.
Here's an underscorejs version with a shorter filter and just giving back the names:
var result = _.chain(test).pluck('team').flatten().filter('checked').pluck('name').value();
// ["Alex", "Tom"]
You can simply use forEach function to handle this
var test = [...];
var result = [];
test.forEach(function(item) {
item.team.forEach(function(worker) {
if(worker.checked) {
result.push(worker.name);
}
})
})
console.log(result)
//Return ["Alex","Tom"]
One way to use filtering for multidimentional array is by using filter and any.
_.filter(test, function(team) {
return _.any(team.team, function(player) {
return player.checked;
});
});