I get json from client side which is nested, I want to faltten it and make the children object keys, preferably using underscore.js.
For example that is my json:
var data = {
or:[
{
dealershipCompany : 11
},
{
authType: 'google'
}],
and: [
{
or: [
{
firstName: {'contains': 'search'}
},
{
lastName: {'contains': 'search'}
},
{
email: {'contains': 'search'}
}]
}]
};
I want to remove both 'or' & 'and'
and when I get the object keys using Object.keys(data) I get
['0','1','2','3','4','5']
but I want it to be like this
['dealershipCompany', 'authType', 'firstName', 'lastName','email']
I tried several times to flatten it by myself but always object keys get numbered
Here the link of jsFiddle
This should work:
var data = {
or:[
{ dealershipCompany : 11 },
{ authType: 'google' }
],
and: [ {
or: [
{ firstName: {'contains': 'search'} },
{ lastName: {'contains': 'search'} },
{ email: {'contains': 'search'} }
]
}]
};
function getOnlyObjects(data) {
var result = [];
if (Array.isArray(data)) {
data.forEach(function(item) {
result = result.concat(
getOnlyObjects(item)
);
});
}
else {
Object.keys(data).forEach(function(key) {
if (Array.isArray(data[key])) {
result = result.concat(
getOnlyObjects(data[key])
);
}
else {
result = result.concat(data);
}
});
}
return result;
}
function getData(data) {
return getOnlyObjects(data).map(function(item) {
return Object.keys(item)[0];
});
}
console.log(getData(data));
Outputs:
["dealershipCompany", "authType", "firstName", "lastName", "email"]
When you use Object.keys over array, you will get indexes, hence you were getting ['0','1','2','3','4','5'].
Edit 1
Have migrated === 'and', === 'or' to an array exceptionList. You can add further keys that you need to filter. This will keep filtering manageable and condition clean.
Code
JSFiddle
var data = {
or: [{
dealershipCompany: 11
}, {
authType: 'google'
}],
and: [{
or: [{
firstName: {
'contains': 'search'
}
}, {
lastName: {
'contains': 'search'
}
}, {
email: {
'contains': 'search'
}
}, ]
}]
};
var result = [];
// You can add further keys that you want to filter
var exceptionList = ["and", "or"];
function getKeys(obj) {
var _keys = Object.keys(obj);
_keys.forEach(function(key) {
// Check if key is either,`and`, `or`, or an index of array.
if (exceptionList.indexOf(key) >=0 || !isNaN(key)) {
getKeys(obj[key]);
} else {
result.push(key);
}
});
}
getKeys(data);
console.log(result)
Related
I have an array
const dataCheck = ["Rohit","Ravi"];
I have another array of object
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
I want to check if any value in dataCheck is present in the userData and then return a new array with the below data
const newData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit", status: "present" },
{ name: "Ravi", status: "present" },
];
I tried to do something using loops but not getting the expected results
const dataCheck = ["Rohit", "Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" }
];
let newDataValue = {};
let newData = [];
userData.forEach((user) => {
const name = user.name;
dataCheck.forEach((userName) => {
if (name === userName) {
newDataValue = {
name: name,
status: "present"
};
} else {
newDataValue = {
name: name
};
}
newData.push(newDataValue);
});
});
console.log(newData);
My trial gives me repeated results multiple results which is just duplicates
You should use map() and a Set.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const set = new Set(dataCheck);
const output = userData.map(data => set.has(data.name) ? ({...data, status: "present"}): data)
console.log(output)
.as-console-wrapper { max-height: 100% !important; top: 0; }
A Set allows for lookups in O(1) time and therefore this algorithm works in O(n) time. If you would use the array for lookups (e.g. using indcludes(), find() etc.) the runtime would be O(n²). Although this will certainly not matter at all for such small arrays, it will become more relevant the larger the array gets.
map() is used here because you want a 1:1 mapping of inputs to outputs. The only thing to determine then is, what the output should be. It is either the input, if the value is not in the Set, or it is the input extended by one property status set to "present". You can check for the presence in a Set using the has() method and can use the ternary operator ? to make the decision which case it is.
const dataCheck = ["Rohit", "Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
// map through every object and check if name property
// exists in data check with help of filter.
// if it exists the length of filter should be 1 so
// you should return { name: el.name, status: "present" } else
// return { name: el.name }
let newData = userData.map((el) => {
if (dataCheck.filter((name) => name === el.name).length > 0) {
return { name: el.name, status: "present" };
} else {
return { name: el.name };
}
});
console.log("newdata: ", newData);
A better approach would be to use map over userData array, find for matching element in dataCheck, if found return matching element + a status key or just return the found element as it is.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const getUpdatedObject = () => {
return userData.map(userData => {
const userDetail = dataCheck.find(data => userData.name === data);
if(userDetail) return {userDetail, status:"present"}
else return {...userData}
});
}
console.log(getUpdatedObject())
Working fiddle
Loop through userData, check if name is includes in dataCheck. If true add status 'present'.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
for (let user of userData) {
if(dataCheck.includes(user.name)) {
user.status = 'present'
}
}
console.log(userData)
You are seeing repeated results due to the second loop dataCheck.forEach((userName) => { as every loop of dataCheck will fire the if/else statement and add something to the final array. However many values you add to dataCheck will be however many duplicates you get.
Only need to loop through one array and check if the value is in the other array so no duplicates get added.
const dataCheck = ["Rohit", "Ravi"];
const userData = [{ name: "Sagar" }, { name: "Vishal" }, { name: "Rohit" }, { name: "Ravi" }];
let newDataValue = {};
let newData = [];
// loop thru the users
userData.forEach((user) => {
// set the user
const name = user.name;
// check if in array
if (dataCheck.indexOf(name) >= 0) {
newDataValue = {
name: name,
status: "present",
};
}
// not in array
else {
newDataValue = {
name: name,
};
}
newData.push(newDataValue);
});
console.log(newData);
So you will do like this :
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const newUserData = userData.map( user => {
dataCheck.forEach( data => {
if( data === user.name )
user.status = "present";
});
return user;
} );
console.log( newUserData );
The array
const users = dedup([
{ id: 1, email: 'foo#example.com' },
{ id: 2, email: 'sho#example.com' },
{ id: 1, email: 'bin#example.com' },
]);
/* would ideally like it to return
Object {
email: "foo#example.com",
email: "bin#example.com",
id:1
}, Object {
email: "sho#example.com",
id:2
} */
The Hash Table
function dedup(arr) {
var hashTable = {};
return arr.filter(function (el) {
var key = JSON.stringify(el);
var match = Boolean(hashTable[key]);
return (match ? false : hashTable[key] = true);
});
}
My returns statement that only filters out exact duplicates and doesn't join similar id's with different email addresses
console.log(users);
/* currently returns
Object {
email: "foo#example.com",
id:1
}, Object {
email: "sho#example.com",
id:2
},
{ id: 1, email: 'bin#example.com' },
]); */
function dedup(arr) {
var hashTable = {};
arr.forEach(function(el) {
if (!hashTable.hasOwnProperty(el.id)) {
hashTable[el.id] = [];
}
hashTable[el.id].push(el.email);
});
return hashTable;
}
Result should be:
{
1: ['bin#example.com', 'foo#example.com' ],
2: ['sho#example.com']
}
Hope this helped.
I figure I shouldn't be having trouble with this, but I am. I am trying to switch up the syntax/variables of a JSON object to match a certain parameters.
Here is the JSON I am working with:
{
"name":"BHPhotovideo",
"prices":[
{
"price":"799.00",
"createdAt":"2017-07-23T16:17:11.000Z",
"updatedAt":"2017-07-23T17:21:41.000Z"
},
{
"price":"770.00",
"createdAt":"2017-07-21T16:17:11.000Z",
"updatedAt":"2017-07-23T16:17:11.000Z"
},
{
"price":"599.00",
"createdAt":"2017-07-19T16:17:11.000Z",
"updatedAt":"2017-07-22T16:17:11.000Z"
},
{
"price":"920.00",
"createdAt":"2017-07-22T16:17:11.000Z",
"updatedAt":"2017-07-22T16:17:11.000Z"
}
]
},
etc...
I am just trying to get the data to be formatted like this:
{
"label":"BHPhotoVideo", // Same as name
"data":[
{
"x":"2017-07-23T16:17:11.000Z", // Same as createdAt
"y":799 // Same as price
},
{
"x":"2017-07-21T16:17:11.000Z",
"y":770
},
{
"x":"2017-07-19T16:17:11.000Z",
"y":599
},
{
"x":"2017-07-22T16:17:11.000Z",
"y":920
}
]
},
etc...
The amount of these objects are dynamic/subject to change, I've been making a mess out of foreach loops and trying to piece this together. I keep coming into errors, what's the best way to approach this?
What about this ?
data.map(
(item) => ({
"label":"BHPhotoVideo", // Same as name
"data": item.prices.map(nested => ( {
"x":nested.createdAt,
"y":nested.price
}))
})
)
Did you want the y values to be integers?
var ar = [
{
"name":"BHPhotovideo",
"prices":[
{
"price":"799.00",
"createdAt":"2017-07-23T16:17:11.000Z",
"updatedAt":"2017-07-23T17:21:41.000Z"
},
{
"price":"770.00",
"createdAt":"2017-07-21T16:17:11.000Z",
"updatedAt":"2017-07-23T16:17:11.000Z"
},
{
"price":"599.00",
"createdAt":"2017-07-19T16:17:11.000Z",
"updatedAt":"2017-07-22T16:17:11.000Z"
},
{
"price":"920.00",
"createdAt":"2017-07-22T16:17:11.000Z",
"updatedAt":"2017-07-22T16:17:11.000Z"
}
]
},
{
"name":"Adorama",
"prices":[
{
"price":"799.00",
"createdAt":"2017-07-22T16:17:11.000Z",
"updatedAt":"2017-07-23T17:21:41.000Z"
},
{
"price":"799.00",
"createdAt":"2017-07-20T16:17:11.000Z",
"updatedAt":"2017-07-23T16:17:11.000Z"
},
{
"price":"810.00",
"createdAt":"2017-07-18T16:17:11.000Z",
"updatedAt":"2017-07-22T16:17:11.000Z"
},
{
"price":"799.00",
"createdAt":"2017-07-17T16:17:11.000Z",
"updatedAt":"2017-07-22T16:17:11.000Z"
}
]
}
];
var out = ar.map( function(a) {
return {
"label" : a.name,
"prices" : a.prices.map( function(aa) { return {x: aa.createdAt, y: aa.price} })
}
});
console.log( out );
map over the original array returning a changed object; returning the name, and a new array from using map over the prices.
const obj2 = obj.map((item) => {
return {
label: item.name,
data: item.prices.map((data) => {
return {
x: data.createdAt,
y: data.price
}
})
}
});
DEMO
This question already has answers here:
How to get distinct values from an array of objects in JavaScript?
(63 answers)
Get all unique values in a JavaScript array (remove duplicates)
(91 answers)
Closed 6 years ago.
I have array with obejcts email and Id so I want delete duplicate elements who have similar ID's.
Example:
var newarray=[
{
Email:"test1#gmail.com",
ID:"A"
},
{
Email:"test2#gmail.com",
ID:"B"
},
{
Email:"test3#gmail.com",
ID:"A"
},
{
Email:"test4#gmail.com",
ID:"C"
},
{
Email:"test4#gmail.com",
ID:"C"
}
];
Now I need to delete Duplicate elements which have ID's are common.In the sence I am expecting final Array is
var FinalArray=[
{
Email:"test1#gmail.com",
ID:"A"
},
{
Email:"test2#gmail.com",
ID:"B"
},
{
Email:"test5#gmail.com",
ID:"C"
}
];
Use Array.prototype.filter to filter out the elements and to keep a check of duplicates use a temp array
var newarray = [{
Email: "test1#gmail.com",
ID: "A"
}, {
Email: "test2#gmail.com",
ID: "B"
}, {
Email: "test3#gmail.com",
ID: "A"
}, {
Email: "test4#gmail.com",
ID: "C"
}, {
Email: "test5#gmail.com",
ID: "C"
}];
// Array to keep track of duplicates
var dups = [];
var arr = newarray.filter(function(el) {
// If it is not a duplicate, return true
if (dups.indexOf(el.ID) == -1) {
dups.push(el.ID);
return true;
}
return false;
});
console.log(arr);
You could filter it with a hash table.
var newarray = [{ Email: "test1#gmail.com", ID: "A" }, { Email: "test2#gmail.com", ID: "B" }, { Email: "test3#gmail.com", ID: "A" }, { Email: "test4#gmail.com", ID: "C" }, { Email: "test5#gmail.com", ID: "C" }],
filtered = newarray.filter(function (a) {
if (!this[a.ID]) {
this[a.ID] = true;
return true;
}
}, Object.create(null));
console.log(filtered);
.as-console-wrapper { max-height: 100% !important; top: 0; }
ES6 with Set
var newarray = [{ Email: "test1#gmail.com", ID: "A" }, { Email: "test2#gmail.com", ID: "B" }, { Email: "test3#gmail.com", ID: "A" }, { Email: "test4#gmail.com", ID: "C" }, { Email: "test5#gmail.com", ID: "C" }],
filtered = newarray.filter((s => a => !s.has(a.ID) && s.add(a.ID))(new Set));
console.log(filtered);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you can use Javascript libraries such as underscore or lodash, I recommend having a look at _.uniq function in their libraries. From lodash:
_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])
Here you have to use like below,
var non_duplidated_data = _.uniq(newarray, 'ID');
Another solution using Array.prototype.reduce and a hash table - see demo below:
var newarray=[ { Email:"test1#gmail.com", ID:"A" }, { Email:"test2#gmail.com", ID:"B" }, { Email:"test3#gmail.com", ID:"A" }, { Email:"test4#gmail.com", ID:"C" }, { Email:"test5#gmail.com", ID:"C" } ];
var result = newarray.reduce(function(hash){
return function(prev,curr){
!hash[curr.ID] && (hash[curr.ID]=prev.push(curr));
return prev;
};
}(Object.create(null)),[]);
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
existing ["562fae5a626ca2e032947baa"]
new array [ { _id: '562fae5a626ca2e032947baa' },
{ _id: '562fae57626ca2e032947ba9' } ]
modified [ { _id: '562fae5a626ca2e032947baa' },
{ _id: '562fae57626ca2e032947ba9' } ]
I have an existing array and a new array, i want to compare the existing and the new array and remove the duplicates.
var existing = ["562fae5a626ca2e032947baa"];
var newArr = [ { _id: '562fae5a626ca2e032947baa' },
{ _id: '562fae57626ca2e032947ba9' } ];
newArr = newArr.filter(function(val){
return existing.indexOf(val) == -1;
});
console.log(newArr);
When i try to print newArr, i still get the two objects?
modified [ { _id: '562fae5a626ca2e032947baa' },
{ _id: '562fae57626ca2e032947ba9' } ]
I want the modified array to have only.
modified [{ _id: '562fae57626ca2e032947ba9' } ]
Below is the fiddle.
http://jsfiddle.net/ema6upg1/2/
newArr.filter(function(val){
return existing.indexOf(val._id) == -1;
})
is what you need, val is an object, you need to compare its _id
Your problem is, that one array contains objects.
var existing = ["562fae5a626ca2e032947baa"];
var newArr = [ { _id: '562fae5a626ca2e032947baa' },
{ _id: '562fae57626ca2e032947ba9' } ];
newArr = newArr.filter(function(val){
if(typeof val == "object") {
return existing.indexOf(val._id) == -1;
}
return existing.indexOf(val) == -1;
});
console.log(newArr);
The lookup for an object property is more efficient than iterating an array to find an id. Consider:
var existing = {
"562fae5a626ca2e032947baa": true
};
var newArr = [ { _id: '562fae5a626ca2e032947baa' },
{ _id: '562fae57626ca2e032947ba9' } ];
newArr.filter(function(val) {
return existing[val._id] || false;
}