I am trying to join an array of dates and values to an array of dates, without filtering out the extra dates. The LinqJS reference here is pretty confusing on how I would actually go about using the join. The question here did not help much either.
Edit:
Join documentation here: https://svschmidt.github.io/linqjs/Collection.html#Join
It looks like I need help sorting out how to get the join to be an outer and not an inner join so it includes null/undefined values.
Say I have two arrays:
Array 1:
[
'2017-02-10',
'2017-02-11',
'2017-02-12',
'2017-02-13',
'2017-02-20',
'2017-02-21',
'2017-02-22',
'2017-02-23',
'2017-02-24',
'2017-02-25',
'2017-02-26',
'2017-02-27'
]
Array 2:
[
{ date: '2017-02-10', value: 5 },
{ date: '2017-02-12', value: 8 },
{ date: '2017-02-13', value: 13 },
{ date: '2017-02-21', value: 14 },
{ date: '2017-02-24', value: 11 },
{ date: '2017-02-27', value: 7 }
]
I want to join them so I get this as my result (- for undefined):
[
'5',
-,
'8',
'13',
-,
'14',
-,
-,
'11',
-,
-,
'7'
]
My current syntax:
Enumerable.from(array1).join(array2, '$', '$.date', "outer, inner => inner.value").toArray()
This results in an array of the values, but the results are still inner joined, and it filters out what might be the null/undefined items.
How can I do this? How does the join syntax work for LinqJS?
I'm not sure about LinqJS, but regular JS is more than capable of doing this.
There are a couple ways to go about it. The reduce(), map(), and sort() functions are very Linq-esque and work well natively. (There is also filter() and a number of others).
const dates = [
'2017-02-10',
'2017-02-11',
'2017-02-12',
'2017-02-13',
'2017-02-20',
'2017-02-21',
'2017-02-22',
'2017-02-23',
'2017-02-24',
'2017-02-25',
'2017-02-26',
'2017-02-27'
]
const data = [
{ date: '2017-02-10', value: 5 },
{ date: '2017-02-12', value: 8 },
{ date: '2017-02-13', value: 13 },
{ date: '2017-02-21', value: 14 },
{ date: '2017-02-24', value: 11 },
{ date: '2017-02-27', value: 7 }
];
const result = dates
.map(date => ({ date, value: '-' }))
.concat(data)
.filter(({ date, value }) => !(data.find(d => d.date === date) && value === '-'))
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
console.log(result);
const justValues = Object.keys(result).map(key => result[key].value);
console.log(justValues);
You could use GroupJoin
Correlates the elements of two sequences based on equality of keys and groups the results. The default equality comparer is used to compare keys.
for a LEFT OUTER JOIN and specify the wanted value and use DefaultIfEmpty
Returns the elements of the specified sequence or the specified value in a singleton collection if the sequence is empty.
with the wanted replacement, if null.
var dates = ['2017-02-10', '2017-02-11', '2017-02-12', '2017-02-13', '2017-02-20', '2017-02-21', '2017-02-22', '2017-02-23', '2017-02-24', '2017-02-25', '2017-02-26', '2017-02-27'],
values = [{ date: '2017-02-10', value: 5 }, { date: '2017-02-12', value: 8 }, { date: '2017-02-13', value: 13 }, { date: '2017-02-21', value: 14 }, { date: '2017-02-24', value: 11 }, { date: '2017-02-27', value: 7 }],
result = Enumerable
.From(dates)
.GroupJoin(
values,
'',
'$.date',
't, u => ({ date: t, value: u.Select("$.value").DefaultIfEmpty("-").ToString() })')
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
ES6
var dates = ['2017-02-10', '2017-02-11', '2017-02-12', '2017-02-13', '2017-02-20', '2017-02-21', '2017-02-22', '2017-02-23', '2017-02-24', '2017-02-25', '2017-02-26', '2017-02-27'],
values = [{ date: '2017-02-10', value: 5 }, { date: '2017-02-12', value: 8 }, { date: '2017-02-13', value: 13 }, { date: '2017-02-21', value: 14 }, { date: '2017-02-24', value: 11 }, { date: '2017-02-27', value: 7 }],
result = Enumerable
.From(dates)
.GroupJoin(
values,
'',
'$.date',
(t, u) => ({
date: t,
value: u.Select("$.value").DefaultIfEmpty("-").ToString()
}))
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
Related
Any help would be appreciated.
I have an array (A) that contains 3 arrays of objects.
A = [
[{date: '2022-12-05', value: 10.6},{date: '2022-12-06', value: 5.6},{date: '2022-12-07, value: 8.6}],
[{date: '2022-12-05', value: 4.2},{date: '2022-12-06', value: 12.3},{date: '2022-12-07, value: 9.5}],
[{date: '2022-12-05', value: 5.6},{date: '2022-12-06', value: 9.8},{date: '2022-12-07, value: 7.8}]
]
From this array (A), I need to get an array (B) that will contain the merged/concatenated arrays with new / renamed object keys (value1,value2,value3) as per the following format:
B = [
{date: '2022-12-05', value1: 10.6, value2: 4.2, value3: 5.6},
{date: '2022-12-06', value1: 5.6, value2: 12.3, value3: 9.8},
{date: '2022-12-07', value1: 8.6, value2: 9.5, value3: 7.8}
]
The length of the first array (A) may vary (1 / 2 / 3 or more), this is why I'm struggling to find a dynamic/efficient solution.
Many thanks
You could take a Map and collect all values.
const
data = [[{ date: '2022-12-05', value: 10.6 }, { date: '2022-12-06', value: 5.6 }, { date: '2022-12-07', value: 8.6 }], [{ date: '2022-12-05', value: 4.2 }, { date: '2022-12-06', value: 12.3 }, { date: '2022-12-07', value: 9.5 }], [{ date: '2022-12-05', value: 5.6 }, { date: '2022-12-06', value: 9.8 }, { date: '2022-12-07', value: 7.8 }]],
result = Array.from(
data
.flat()
.reduce((m, { date, value }) => m.set(date, [...m.get(date) || [], value]), new Map),
([date, values]) => values.reduce((o, v, i) => ({ ...o, [`value${i + 1}`]: v }), { date })
)
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use map() and reduce() functions:
const A = [
[{date: '2022-12-05', value: 10.6},{date: '2022-12-06', value: 5.6},{date: '2022-12-07', value: 8.6}],
[{date: '2022-12-05', value: 4.2},{date: '2022-12-06', value: 12.3},{date: '2022-12-07', value: 9.5}],
[{date: '2022-12-05', value: 5.6},{date: '2022-12-06', value: 9.8},{date: '2022-12-07', value: 7.8}]
];
const B = A.reduce((acc, curr) => {
curr.forEach(item => {
const existingItem = acc.find(i => i.date === item.date);
if (existingItem) {
existingItem[`value${A.indexOf(curr) + 1}`] = item.value;
} else {
acc.push({
date: item.date,
[`value${A.indexOf(curr) + 1}`]: item.value
});
}
});
return acc;
}, []);
console.log(B);
May i suggest a simpler data structure? Instead of numbering through the values in the form of 'value1, 'value2', ... 'valueN', you could use an array of values per date, which would make the transformation also a little bit easier as well:
const A = [
[{date: '2022-12-05', value: 10.6},{date: '2022-12-06', value: 5.6},{date: '2022-12-07', value: 8.6}],
[{date: '2022-12-05', value: 4.2},{date: '2022-12-06', value: 12.3},{date: '2022-12-07', value: 9.5}],
[{date: '2022-12-05', value: 5.6},{date: '2022-12-06', value: 9.8},{date: '2022-12-07', value: 7.8}]
]
//Flatten Array
const Af = A.flat();
//Group by date
const B = Af.reduce((acc, cur) => {
acc[cur['date']] = acc[cur['date']] ? [...acc[cur['date']], cur.value] : [cur.value]
return acc;
}, {});
console.log(B);
I have this dummy MySQL data:
id date (y/m/d) value
1 2022-1-1 random value
2 2022-2-5 random value
3 2022-3-3 random value
4 2022-4-6 random value
5 2022-5-11 random value
6 2022-6-7 random value
7 2022-7-16 random value
8 2022-8-4 random value
9 2022-9-7 random value
10 2022-10-8 random value
11 2022-11-4 random value
12 2022-12-9 random value
13 2023-1-2 random value
14 2023-2-4 random value
15 2023-3-22 random value
16 2023-4-5 random value
17 2023-5-8 random value
18 2023-6-19 random value
19 2023-7-12 random value
20 2023-8-4 random value
21 2023-9-2 random value
22 2023-10-10 random value
23 2023-11-21 random value
24 2023-12-27 random value
I want to achieve something like this:
[{
year:2022,
value:[
{
month:1,
value:[
{
day:1,
value:'random value'
}
]
},
{
month:2,
value:[
{
day:5,
value:'random value'
}
],
...
}
]
},{
year:2023,
value:[
...
]
}]
Is there anything that can sort something like this? I am using JavaScript, Node.js and I get an array of data like the dummy data from MySQL query. I have to sort it like this for frontend React.
I need something that is fast and not require a lot of processing because this operation will be done many times on the server.
Do you have any ideas?
Thank you in advance. :)
You could take an object for grouping and get a nested structure by taking parts of the date.
const
data = [{ id: 1, date: '2022-01-01', value: 'random value' }, { id: 2, date: '2022-02-05', value: 'random value' }, { id: 3, date: '2022-03-03', value: 'random value' }, { id: 4, date: '2022-04-06', value: 'random value' }, { id: 5, date: '2022-05-11', value: 'random value' }, { id: 6, date: '2022-06-07', value: 'random value' }, { id: 7, date: '2022-07-16', value: 'random value' }, { id: 8, date: '2022-08-04', value: 'random value' }, { id: 9, date: '2022-09-07', value: 'random value' }, { id: 10, date: '2022-10-08', value: 'random value' }, { id: 11, date: '2022-11-04', value: 'random value' }, { id: 12, date: '2022-12-09', value: 'random value' }, { id: 13, date: '2023-01-02', value: 'random value' }, { id: 14, date: '2023-02-04', value: 'random value' }, { id: 15, date: '2023-03-22', value: 'random value' }, { id: 16, date: '2023-04-05', value: 'random value' }, { id: 17, date: '2023-05-08', value: 'random value' }, { id: 18, date: '2023-06-19', value: 'random value' }, { id: 19, date: '2023-07-12', value: 'random value' }, { id: 20, date: '2023-08-04', value: 'random value' }, { id: 21, date: '2023-09-02', value: 'random value' }, { id: 22, date: '2023-10-10', value: 'random value' }, { id: 23, date: '2023-11-21', value: 'random value' }, { id: 24, date: '2023-12-27', value: 'random value' }],
keys = ['year', 'month', 'day'],
result = data
.reduce((r, { date, value }) => {
date.split('-').reduce(function(level, key, i, a) {
if (!level[key]) {
level[key] = { _: [] };
level._.push(i + 1 === a.length
? { [keys[i]]: key, value }
: { [keys[i]]: key, children: level[key]._ }
);
}
return level[key];
}, r);
return r;
}, { _: [] })
._;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Those look like dates. Think about date arithmetic:
'2020-01-01' + INTERVAL FLOOR(RAND() * 1000) DAY
will generate one random date within 1000 days after the start of 2020. (Note there may be dups, but they could be dealt with afterward.)
This technique will generate Jan 31, but not Feb 31. It looks like your approach leads to losing Jan 31 or creating Feb 31.
Also, your data looks like you want one day per month. Is that a requirement. If so, please state. (My technique does not lend itself to such.)
I have 2 arrays with current week dates and investments with value and date. I want to return an array with the values that have corresponding dates between the 2 arrays.
My non-working solution is:
const daysOfWeek = [
"20-06-2022",
"21-06-2022",
"22-06-2022",
"23-06-2022",
"24-06-2022",
"25-06-2022",
"26-06-2022",
]
const investmentsData = [{
value: 0.77,
date: "21-06-2022"
},
{
value: 1.50,
date: "22-06-2022"
},
{
value: 0.80,
date: "20-06-2022"
},
{
value: 1.00,
date: "21-06-2022"
},
{
value: 0.77,
date: "20-06-2022"
},
{
value: 0.79,
date: "22-06-2022"
},
{
value: 0.73,
date: "18-06-2022"
},
{
value: 1.29,
date: "19-06-2022"
}
]
const result = investmentsData.flatMap((dayValue) => {
const getDayValue = daysOfWeek.filter((day) => {
return dayValue.date === day;
});
return getDayValue;
});
const filteredResult = result.filter((val) => !!val);
console.log(filteredResult)
// ["21-06-2022", "22-06-2022", "20-06-2022", "21-06-2022", "20-06-2022", "22-06-2022"]
When what I need is:
[0.77, 1.50, 0.80, 1.00, 0.77, 0.79]
Probably the filter inside the map is not the best option as it´s going to return the value of the first array (which is a date).
I also have the problem that result returns also the undefined. I then run filteredResult to remove all the undefined in the result. I guess this is a job that can be done with one function all together.
Take it step by step:
Filter investmentsData on whether or not daysOfWeek contains the date
From the filtered values, return the value.
const daysOfWeek = ["20-06-2022", "21-06-2022", "22-06-2022", "23-06-2022", "24-06-2022", "25-06-2022", "26-06-2022"];
const investmentsData = [
{ value: 0.77, date: "21-06-2022" },
{ value: 1.50, date: "22-06-2022" },
{ value: 0.80, date: "20-06-2022" },
{ value: 1.00, date: "21-06-2022" },
{ value: 0.77, date: "20-06-2022" },
{ value: 0.79, date: "22-06-2022" },
{ value: 0.73, date: "18-06-2022" },
{ value: 1.29, date: "19-06-2022" }
]
const result = investmentsData
.filter(d => daysOfWeek.includes(d.date))
.map(d => d.value);
console.log(result);
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 10 months ago.
Improve this question
I have a very specific problem in Javascript. I have array of objects, and each object contains some date.
const array = [
{name: 'first', date: '2020-05-06'},
{name: 'second', date: '2020-04-07'},
{name: 'third', date: '2020-05-06'},
{name: 'fourth', date: '2020-04-07'},
{name: 'fifth', date: '2020-04-09'}
]
For each unique date in array I need to make an object with key of that unique date and put all the elements of array in that object:
[{name: 'first', date: '2020-05-06'}, {name: 'third', date: '2020-05-06'}]
[{name: 'second', date: '2020-04-07'} {name: 'fourth', date: '2020-04-07'}]
[{name: 'fifth', date: '2020-04-09'}]
Any help would mean a lot. Thank you all!
Almost any array specific task which somehow involves grouping will be solved most convenient by a reduce based approach.
Regardless of the OP's final data structure one would start with the aggregation of an object which is going to feature date specific entries where each key represents a unique date value and each value is an array of items of such same dates.
In order to get an array of such grouped arrays one would pass the reduced object to Object.values.
One of cause could always enforce a date specific item precedence by applying a sort task first.
function groupAndCollectSameDateItem(result, item) {
// access item date.
const { date } = item;
// access or create `date` specific
// group array and collect item.
(result[date] ??= []).push(item);
return result;
}
const dateItems = [
{ name: 'first', date: '2020-05-06' },
{ name: 'second', date: '2020-04-07' },
{ name: 'third', date: '2020-05-06' },
{ name: 'fourth', date: '2020-04-07' },
{ name: 'fifth', date: '2020-04-09' },
];
console.log(
'index/map of arrays of same date items grouped by a date key ...',
dateItems
.reduce(groupAndCollectSameDateItem, {})
);
console.log(
'sort items in ascending date order before creating the index ...',
dateItems
.sort((a, b) =>
new Date(a.date).getTime() - new Date(b.date).getTime()
)
.reduce(groupAndCollectSameDateItem, {})
);
console.log(
'array of arrays of same date items in ascending date order ...',
Object
.values(
dateItems
.sort((a, b) =>
new Date(a.date).getTime() - new Date(b.date).getTime()
)
.reduce(groupAndCollectSameDateItem, {})
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Edit
"... Our project eslint does not allow the ??= symbol. –
Marin"
The alternative implementation for not using the Logical nullish assignment operator might look similar to the next example code ...
function groupAndCollectSameDateItem(result, item) {
// access item date.
const { date } = item;
// access or create `date` specific
// group array and collect item.
let groupList = result[date];
if (!groupList) {
groupList = result[date] = [];
}
groupList.push(item)
return result;
}
const dateItems = [
{ name: 'first', date: '2020-05-06' },
{ name: 'second', date: '2020-04-07' },
{ name: 'third', date: '2020-05-06' },
{ name: 'fourth', date: '2020-04-07' },
{ name: 'fifth', date: '2020-04-09' },
];
console.log(
'index/map of arrays of same date items grouped by a date key ...',
dateItems
.reduce(groupAndCollectSameDateItem, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
you can use .reduce(), i leave you this example:
let foo = [
{name: 'first', date: '2020-05-06'},
{name: 'second', date: '2020-04-07'},
{name: 'third', date: '2020-05-06'},
{name: 'fourth', date: '2020-04-07'},
{name: 'fifth', date: '2020-04-09'}
];
let boo = foo.reduce((acc, elem) => {
(acc[elem.date] ??= []).push(elem);
// since OP said they have eslint issues with ??=
// following may be used instead of the above line
// acc[elem.date] = (acc[elem.date] || []).concat([elem])
return acc;
}, {});
console.log('output: ', boo);
result:
{
'2020-05-06': [
{ name: 'first', date: '2020-05-06' },
{ name: 'third', date: '2020-05-06' }
],
'2020-04-07': [
{ name: 'second', date: '2020-04-07' },
{ name: 'fourth', date: '2020-04-07' }
],
'2020-04-09': [ { name: 'fifth', date: '2020-04-09' } ]
}
What you need is a groupBy implementation. Lodash library has it https://lodash.com/docs/#groupBy
Example: https://stackblitz.com/edit/js-twywom?file=index.js
This question already has answers here:
How to sort an array of objects by date?
(4 answers)
Closed 2 years ago.
I have an array :
const arr = [
{ name: 'abc', date: '30/03/2014' },
{ name: 'cde', date: '30/03/2015' },
{ name: 'fgh', date: '20/04/2014' },
{ name: 'xyz', date: '17/09/2014' },
];
How can I sort this array so that the output would be like this:
const arr = [
{ name: 'cde', date: '30/03/2015' },
{ name: 'xyz', date: '17/09/2014' },
{ name: 'fgh', date: '20/04/2014' },
{ name: 'abc', date: '30/03/2014' },
];
// sort the array with date in latest first.
Using Array#sort with your own sorting comperator. For this split the dates and build with taht in the right sequence a new Date which can be compared.
const arr = [
{ name: 'abc', date: '30/03/2014' },
{ name: 'cde', date: '30/03/2015' },
{ name: 'fgh', date: '20/04/2014' },
{ name: 'xyz', date: '17/09/2014' },
];
arr.sort((a,b) => {
let tempA = a.date.split('/');
let tempB = b.date.split('/');
return ((new Date(tempB[2],tempB[1],tempB[0])) - (new Date(tempA[2],tempA[1],tempA[0])));
});
console.log(arr);