Related
i'm trying to duplicate objects based on two properties that have multiple values differentiated by a comma.
For example:
I have an object
const obj = {
id: 1
date: "2021"
tst1: "111, 222"
tst2: "AAA, BBB"
}
And I would like the result to be an array of 2 objects in this case (because there are 2 values in tst1 OR tst2, these 2 properties will always have the same nr of values differentiated by a comma)
[{
id: 1,
date: "2021",
tst1: "111",
tst2: "AAA",
},
{
id: 1,
date: "2021",
tst1: "222",
tst2: "BBB",
}]
What I tried is this:
I created a temporary object
const tempObject = {
id: obj.id,
date: obj.date,
}
And then I would split and map the property that has multiple values, like this:
cont newObj = obj.tst1.split(",").map(function(value) {
let finalObj = {}
return finalObj = {
id: tempObject.id,
date: tempObject.date,
tst1: value,
})
And now, the newObj is an array of objects and each object contains a value of tst1.
The problem is I still have to do the same for the tst2...
And I was wondering if there is a simpler method to do this...
Thank you!
Here is an example that accepts an array of duplicate keys to differentiate. It first maps them to arrays of entries by splitting on ',' and then trimming the entries, then zips them by index to create sub-arrays of each specified property, finally it returns a result of the original object spread against an Object.fromEntries of the zipped properties.
const mapDuplicateProps = (obj, props) => {
const splitProps = props.map((p) =>
obj[p].split(',').map((s) => [p, s.trim()])
);
// [ [[ 'tst1', '111' ], [ 'tst1', '222' ]], [[ 'tst2', 'AAA' ], [ 'tst2', 'BBB' ]] ]
const dupeEntries = splitProps[0].map((_, i) => splitProps.map((p) => p[i]));
// [ [[ 'tst1', '111' ], [ 'tst2', 'AAA' ]], [[ 'tst1', '222' ], [ 'tst2', 'BBB' ]] ]
return dupeEntries.map((d) => ({ ...obj, ...Object.fromEntries(d) }));
};
const obj = {
id: 1,
date: '2021',
tst1: '111, 222',
tst2: 'AAA, BBB',
};
console.log(mapDuplicateProps(obj, ['tst1', 'tst2']));
Not sure if that's what you're searching for, but I tried making a more general use of what you try to do:
const duplicateProperties = obj => {
const properties = Object.entries(obj);
let acc = [{}];
properties.forEach(([key, value]) => {
if (typeof value === 'string' && value.includes(',')) {
const values = value.split(',');
values.forEach((v, i) => {
if (!acc[i]) {
acc[i] = {};
}
acc[i][key] = v.trim();
});
} else {
acc.forEach(o => o[key] = value);
}
});
return acc;
};
const obj = {
id: 1,
date: '2021',
tst1: '111, 222',
tst2: 'AAA, BBB',
};
console.log(duplicateProperties(obj));
You could start by determining the length of the result using Math.max(), String.split() etc.
Then you'd create an Array using Array.from(), returning the correct object for each value of the output index.
const obj = {
id: 1,
date: "2021",
tst1: "111, 222",
tst2: "AAA, BBB",
}
// Determine the length of our output array...
const length = Math.max(...Object.values(obj).map(s => (s + '').split(',').length))
// Map the object using the relevant index...
const result = Array.from({ length }, (_, idx) => {
return Object.fromEntries(Object.entries(obj).map(([key, value]) => {
const a = (value + '').split(/,\s*/);
return [key, a.length > 1 ? a[idx] : value ]
}))
})
console.log(result)
.as-console-wrapper { max-height: 100% !important; }
Is it possible to check if an object in an array has a specific Date value in the following data format?
I have an array like this for example
[
{
date: '2020-05-03',
items: [{}...]
},
...
]
-> In the above array, I want to check if any object in the array has '2020-05-03' as the date value.
And I want to find out if an object with Date is ??
I tried the following, but this only confirms that it has a 'date' as the key, and I couldn't compare values.
const existsDate = _.find(prev, 'date');
I also want to push an item to the items of the object containing that date if a date already exists.
If not, i need to create a new date object.
you caan use filter or find function
let arr = [
{data:'2020-05-23'},
{data:'2020-06-23'},
{data:'2020-07-23'}
]
let find = arr.find( (val) => val.data == '2020-06-23')
console.log(find)
You can make use of Array.prototype.find and do a string comparison
var arr = [
{
date: '2020-05-03',
items: [{}...]
},
]
const obj = arr.find(item => item.data === '2020-05-03');
EDIT: Since you want to update the existing array, you would need to make use of slice with findIndex to update array
var arr = [
{
date: '2020-05-03',
items: [{}...]
},
]
const newItem = {};
const index= arr.findIndex(item => item.data === '2020-05-03');
if(index > -1) {
arr = [...arr.slice(0, index), {...arr[index], items: arr[index].items.concat(newItems), ...arr.slice(index + 1)}
} else {
arr.push({data: new Date(), items: [newItem]})
}
You can use, Array find(), some() to get/check the required conditions, for example:
const arr = [
{
date: '2020-05-03',
items: [{}]
},
{
date: '2020-05-02',
items: [{}]
}
]
function checkAndAdd(date) {
const res = arr.find(ob => ob.date === date);
// console.log(res);
// based on the added comments.
// if date is found, push something to the items, list:
if (res) {
res.items.push('Hello');
} else {
arr.push({
date,
items: [{}]
})
}
console.log('finalArray', arr);
}
checkAndAdd('2020-05-03');
checkAndAdd('2020-05-06');
I'm not sure what exactly you are looking to do but his code iterates through the array of objects and checks against the date variable. It outputs the index (i).
let date = "2020-05-03"
const array = [
{ date: "2020-05-01" },
{ date: "2020-05-02" },
{ date: "2020-05-03", },
]
for(let i = 0 ; i < array.length ; i ++) {
if(array[i].date === date) {
console.log(i); }
else {
console.log("Date not in array");
}
}
I need to get all unique days of multiple date values in the format DD.MM.. In this example data, there are two values for the 24th of december:
const data = [
{ date: ISODate("2019-12-24T03:24:00Z") },
{ date: ISODate("2019-12-24T04:56:00Z") },
{ date: ISODate("2019-12-25T02:34:00Z") },
{ date: ISODate("2019-12-26T01:23:00Z") }
]
So the result should be
const result = [
'24.12.',
'25.12.',
'26.12.'
]
So first of all I'll map my data and split the values only for the dates:
const dates = data.map(d => d.date.toString().split('T')[0])
But how do I get the unique values and change the output format?
Update
I came up with this, but it looks very complicated...
data.map(d => {
const dateSplit = d.date.toString().split('T')[0].split('-')
return dateSplit[2] + '.' + dateSplit[1] + '.'
})
.filter((value, index, self) {
return self.indexOf(value) === index
})
It seems that ISODate returns a standard JS Date object. You can use Date.getDate() to get the day, and Date.getMonth() to get the month (0 based, so we need to add 1):
const data = [
{ date: new Date('2019-12-24T03:24:00Z') },
{ date: new Date('2019-12-24T04:56:00Z') },
{ date: new Date('2019-12-25T02:34:00Z') },
{ date: new Date('2019-12-26T01:23:00Z') }
]
const result = [...new Set(data.map(({ date: d }) =>
`${d.getDate()}.${d.getMonth() + 1}.`
))]
console.log(result)
Previous answer:
Use a regular expression to match the month and the day, and assign them to consts using destructuring. Assemble the string using template literal. Remove duplicates by assigning the values to a Set, and then spreading back to an array.
Note: Since I don't have access to the ISODate, I've removed it. I left .toString() although it's not needed in this example, but will be needed when used with ISODate.
const data = [
{ date: '2019-12-24T03:24:00Z' },
{ date: '2019-12-24T04:56:00Z' },
{ date: '2019-12-25T02:34:00Z' },
{ date: '2019-12-26T01:23:00Z' }
]
const pattern = /-([0-9]{2})-([0-9]{2})T/
const result = [...new Set(data.map(d => {
const [, mon, day] = d.date.toString().match(pattern)
return `${day}.${mon}.`;
}))]
console.log(result)
Use .filter() to filter through only values that are the first of their value.
//temporary function
const ISODate = (d) => d;
const data = [{
date: ISODate("2019-12-24T03:24:00Z")
},
{
date: ISODate("2019-12-24T04:56:00Z")
},
{
date: ISODate("2019-12-25T02:34:00Z")
},
{
date: ISODate("2019-12-26T01:23:00Z")
}
]
const dates = data.map(d => d.date.toString().split('T')[0].split("-").slice(1, 3).reverse().join(".") + ".")
console.log(dates.filter((v, i, a) => a.indexOf(v) === i));
You can do this pretty easily by using Array.reduce. Note that I converted ISODate to be Date since I don't have that class, but it should be the same concept.
const data = [
{ date: new Date("2019-12-24T03:24:00Z") },
{ date: new Date("2019-12-24T04:56:00Z") },
{ date: new Date("2019-12-25T02:34:00Z") },
{ date: new Date("2019-12-26T01:23:00Z") }
];
const result = data.reduce( (acc, curr) => {
if (acc.length > 0) {
const hasDate = acc.find(d => d.date.getMonth() === curr.date.getMonth() && d.date.getDate() === curr.date.getDate());
if (!hasDate) { acc.push(curr); }
} else {
acc.push(curr);
}
return acc;
}, []);
console.log(result);
I would use the uniq function in the Underscore.js library:
const data = [
{ date: ISODate("2019-12-24T03:24:00Z") },
{ date: ISODate("2019-12-24T04:56:00Z") },
{ date: ISODate("2019-12-25T02:34:00Z") },
{ date: ISODate("2019-12-26T01:23:00Z") }
];
let dates = _.uniq(data.map(d => d.date.toString().split('T')[0]));
A nice considerable way is:
const array = [1, 2, 6, 5,5, 5, 3, 7, 8];
const uniqueKeys = array.reduce((hashMap, value) => {
if (!hashMap[value]) {
hashMap[value] = true;
}
return hashMap;
}, {});
const uniqueValues = Object.keys(uniqueKeys);
console.log(uniqueValues);
It is nice because it iterates the array once, instead of x * x (a.k.a log(n) instead of log(n^2) as with .filter() example
const array = [1, 2, 6, 5,5, 5, 3, 7, 8];
const uniqueKeys = array.reduce((hashMap, value) => {
if (!hashMap[value]) {
hashMap[value] = true;
}
return hashMap;
}, {});
const uniqueValues = Object.keys(uniqueKeys);
console.log(uniqueValues);
I am getting a jsonResponse with some data like this:
[
{
"date":"2019-02-15",
"value":"5456"
},
{
"date":"2019-02-15",
"value":"5456"
},
{
"date":"2019-02-15",
"value":"5456"
},
{
"date":"2019-02-15",
"value":"-5456"
}
]
What I need is to change the delimiter in the date from " - " to " / ". I need date like: 2019/03/14
I tried to change them like this:
json.replace(/-/g, "/");
and it works but there is a problem. If for some reason the value data is negative -524. That minus will change also.
There si any way to change the delimiter without affecting another data ?
I need to keep the jsonResponse but with the delimiter change.
You have to loop through the array using Array.prototype.map and change the date :
const response = [
{
date: "2019-02-15",
value: "5456"
},
{
date: "2019-02-15",
value: "5456"
},
{
date: "2019-02-15",
value: "5456"
},
{
date: "2019-02-15",
value: "-5456"
}
];
const result = response.map(obj => ({ ...obj, date: obj.date.replace(/-/g, "/") }));
console.log(result);
Use Array.prototype.map to iterate over the objects in the array.
Format the date property by calling String.prototype.split("-") to split the date string it into an array using the - separator.
Then join the array using / by calling Array.prtotype.join("/"):
const data = [
{
"date":"2019-02-15",
"value":"5456"
},
{
"date":"2019-02-15",
"value":"5456"
},
{
"date":"2019-02-15",
"value":"5456"
},
{
"date":"2019-02-15",
"value":"-5456"
}
];
//This function does not modify the original object or the array
function formatDate(){
return data.map(o => ({...o, date: o.date.split("-").join("/")}));
}
console.log(formatDate(data));
you can try map
let response = json.map(doc => doc.date.replace(/-/g, "/"))
I need to transmit some data, that has too many key-value pairs.
As the keys are similar, I dont want to transmit them with each object.
Consider I have the following data:
[
{
x:11,
y:12
},{
x:21,
y:22
},{
x:31,
y:32
},{
x:41,
y:42
}
];
And I need the final output as
[ [x,y],[[11,12],[21,22],[31,32],[41,42]] ] OR
[ [x,y],[11,12],[21,22],[31,32],[41,42] ]
On the other end, I should be able to convert back to its original form.
It would be great if it can handle an additional key in some of the objects
I think I have seen lodash or underscore function for something close/similar to this, but I'm not able to find it right now.
NOTE: I don't know what the keys will be
Lodash v4.17.1
modify original
var modifiedOriginal = _.chain(original)
.map(_.keys)
.flatten()
.uniq()
.thru(function(header){
return _.concat(
[header],
_.map(original, function(item) {
return _.chain(item)
.defaults(_.zipObject(
header,
_.times(_.size(header), _.constant(undefined))
))
.pick(header)
.values()
.value()
})
);
})
.value();
modified back to original (keys order is not
guarantee)
var backToOriginal = _.map(_.tail(modified), function(item) {
return _.chain(_.head(modified))
.zipObject(item)
.transform(function(result, val, key) {
if (!_.isUndefined(val)) {
result[key] = val;
}
})
.value();
});
JSFiddle code https://jsfiddle.net/wa8kaL5g/1/
Using Array#reduce
var arr = [{
x: 11,
y: 12
}, {
x: 21,
y: 22
}, {
x: 31,
y: 32
}, {
x: 41,
y: 42
}];
var keys = Object.keys(arr[0]);
var op = arr.reduce(function(a, b) {
var arr = keys.reduce(function(x, y) {
return x.concat([b[y]]);
}, [])
return a.concat([arr]);
}, [keys]); //If all the objects are having identical keys!
console.log(JSON.stringify(op));
A little more verbose way of doing it:
[Edit: added the function to convert it back]
function convert(arr) {
var retArr = [ [/* keys (retArr[0]) */], [/* values (retArr[1]) */] ]
arr.forEach(function(obj){
// create new array for new sets of values
retArr[1].push([])
// put all of the keys in the correct array
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
// does the key exist in the array yet?
if (retArr[0].indexOf(key) === -1) {
retArr[0].push(key)
}
// get last index of retArr[1] and push on the values
retArr[1][retArr[1].length - 1].push(obj[key])
}
}
})
return retArr
}
function reConvert(arr) {
var retArr = []
var keys = arr[0]
arr[1].forEach(function(itemArr){
var obj = {}
itemArr.forEach(function(item, i){
obj[keys[i]] = item
})
retArr.push(obj)
})
return retArr
}
var objArr = [
{
x:11,
y:12
},{
x:21,
y:22
},{
x:31,
y:32
},{
x:41,
y:42
}
]
var arrFromObj = convert(objArr)
var objFromArr = reConvert(arrFromObj)
console.log(arrFromObj)
console.log(objFromArr)
A solution using Underscore.
First work out what the keys are:
var keys = _.chain(data)
.map(_.keys)
.flatten()
.uniq()
.value();
Then map across the data to pick out the value for each key:
var result = [
keys,
_.map(data, item => _.map(keys, key => item[key]))
];
and back again:
var thereAndBackAgain = _.map(result[1], item => _.omit(_.object(result[0], item), _.isUndefined));
Lodash's version of object is zipObject and omit using a predicate is omitBy:
var thereAndBackAgain = _.map(result[1], item => _.omitBy(_.zipObject(result[0], item), _.isUndefined));
var data = [
{
x:11,
y:12,
aa: 9
},{
x:21,
y:22
},{
x:31,
y:32,
z: 0
},{
x:41,
y:42
}
];
var keys = _.chain(data)
.map(_.keys)
.flatten()
.uniq()
.value();
var result = [
keys,
_.map(data, item => _.map(keys, key => item[key]))
];
var thereAndBackAgain = _.map(result[1], item => _.omit(_.object(result[0], item), _.isUndefined));
console.log(result)
console.log(thereAndBackAgain)
<script src="
https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
In ES6 you can do it by reducing it with Object.values(), and Object.keys(). You can restore it using a combination of Array.prototype.map() and Array.prototype.reduce():
const convertStructure = (data) => data.reduce((s, item) => {
s[1].push(Object.values(item));
return s;
}, [Object.keys(data[0]), []]); // all objects should be the same, so we can take the keys from the 1st object
const restoreStructure = ([keys, data]) => data.map((item) => item.reduce((o, v, i) => {
o[keys[i]] = v;
return o;
}, {}));
const data = [{
x: 11,
y: 12
}, {
x: 21,
y: 22
}, {
x: 31,
y: 32
}, {
x: 41,
y: 42
}];
const convertedStructure = convertStructure(data);
console.log('convertedStructure:\n', convertedStructure);
const restoredStructure = restoreStructure(convertedStructure);
console.log('restoredStructure:\n', restoredStructure);