I have two arrays, array1 has datetime data count by minute from 8am to 2pm and array2 has datetime data count by hour from same date 8am to 1pm.
I want to output the index number of two array that has same datetime.hour. and it should matchup the last available index number of array2 for all of the datetime data from array1 that later than array2.
for example if I have two datetime array like this:
DateTime[] dateTimes1 = new DateTime[]
{
new DateTime(2010, 10, 1, 8, 15, 0),
new DateTime(2010, 10, 1, 8, 30, 1),
new DateTime(2010, 10, 1, 8, 45, 2),
new DateTime(2010, 10, 1, 9, 15, 3),
new DateTime(2010, 10, 1, 9, 30, 4),
new DateTime(2010, 10, 1, 9, 45, 5),
new DateTime(2010, 10, 1, 10, 15, 6),
new DateTime(2010, 10, 1, 10, 30, 7),
new DateTime(2010, 10, 1, 10, 45, 8),
new DateTime(2010, 10, 1, 11, 15, 9),
new DateTime(2010, 10, 1, 11, 30, 10),
new DateTime(2010, 10, 1, 11, 45, 11),
new DateTime(2010, 10, 1, 12, 15, 12),
new DateTime(2010, 10, 1, 12, 30, 13),
new DateTime(2010, 10, 1, 12, 45, 14),
new DateTime(2010, 10, 1, 13, 15, 15),
new DateTime(2010, 10, 1, 13, 30, 16),
new DateTime(2010, 10, 1, 13, 45, 17),
new DateTime(2010, 10, 1, 14, 15, 18),
new DateTime(2010, 10, 1, 14, 30, 19),
new DateTime(2010, 10, 1, 14, 45, 20),
};
DateTime[] dateTimes2 = new DateTime[]
{
new DateTime(2010, 10, 1, 8, 0, 0),
new DateTime(2010, 10, 1, 9, 0, 1),
new DateTime(2010, 10, 1, 10, 0, 2),
new DateTime(2010, 10, 1, 11, 0, 3),
new DateTime(2010, 10, 1, 12, 0, 4),
new DateTime(2010, 10, 1, 13, 0, 5),
};
it should gives me the output:
0, 0
1, 0
2, 0
3, 1
4, 1
5, 1
6, 2
7, 2
8, 2
9, 3
10, 3
11, 3
12, 4
13, 4
14, 4
15, 5
16, 5
17, 5
18, 5
19, 5
20, 5
This is what I have tried:
int i = 0;
int j = 0;
while (i < dateTimes1.Length && j < dateTimes2.Length)
{
if (dateTimes1[i].Date == dateTimes2[j].Date && dateTimes1[i].Hour == dateTimes2[j].Hour)
{
list.Add(i);
list2.Add(j);
i++;
}
else if (dateTimes1[i] < dateTimes2[j])
{
i++;
}
else if (dateTimes1[i] > dateTimes2[j])
{
j++;
}
}
for (int k = 0; k < list.Count; k++)
{
Console.WriteLine(list[k] + " , " + list2[k];
}
but it won't output the index number after 1pm.
Your two lists are not the same length. In your while statement you are trying to iterate two different length lists at the same time.
If I understand your requirements properly you should be doing something like this by using an inner loop:
DateTime[] dateTimes1 = new DateTime[]
{
new DateTime(2010, 10, 1, 8, 15, 0),
new DateTime(2010, 10, 1, 8, 30, 1),
new DateTime(2010, 10, 1, 8, 45, 2),
new DateTime(2010, 10, 1, 9, 15, 3),
new DateTime(2010, 10, 1, 9, 30, 4),
new DateTime(2010, 10, 1, 9, 45, 5),
new DateTime(2010, 10, 1, 10, 15, 6),
new DateTime(2010, 10, 1, 10, 30, 7),
new DateTime(2010, 10, 1, 10, 45, 8),
new DateTime(2010, 10, 1, 11, 15, 9),
new DateTime(2010, 10, 1, 11, 30, 10),
new DateTime(2010, 10, 1, 11, 45, 11),
new DateTime(2010, 10, 1, 12, 15, 12),
new DateTime(2010, 10, 1, 12, 30, 13),
new DateTime(2010, 10, 1, 12, 45, 14),
new DateTime(2010, 10, 1, 13, 15, 15),
new DateTime(2010, 10, 1, 13, 30, 16),
new DateTime(2010, 10, 1, 13, 45, 17),
new DateTime(2010, 10, 1, 14, 15, 18),
new DateTime(2010, 10, 1, 14, 30, 19),
new DateTime(2010, 10, 1, 14, 45, 20),
};
DateTime[] dateTimes2 = new DateTime[]
{
new DateTime(2010, 10, 1, 8, 0, 0),
new DateTime(2010, 10, 1, 9, 0, 1),
new DateTime(2010, 10, 1, 10, 0, 2),
new DateTime(2010, 10, 1, 11, 0, 3),
new DateTime(2010, 10, 1, 12, 0, 4),
new DateTime(2010, 10, 1, 13, 0, 5),
};
int i = 0;
while (i < dateTimes1.Length)
{
int j = 0;
while (j < dateTimes2.Length))
{
if (dateTimes1[i].Date == dateTimes2[j].Date && dateTimes1[i].Hour == dateTimes2[j].Hour)
{
list.Add(i);
list2.Add(j);
i++;
}
else if (dateTimes1[i] < dateTimes2[j])
{
i++;
}
else if (dateTimes1[i] > dateTimes2[j])
{
j++;
}
}
}
for (int k = 0; k < list.Count; k++)
{
Console.WriteLine(list[k] + " , " + list2[k];
}
Here's a pretty basic method using Array.FindIndex and foreach:
EDIT: Updated this answer to handle the "matchup the last available index number of array2 for all of the datetime data from array1 that later than array2." issue.
foreach (DateTime dt in dateTimes1)
{
int currentHour = dt.Hour;
int lastHour = dateTimes2[dateTimes2.GetUpperBound(0)].Hour; //GetUpperBound(0) is the last index
int dt1index = Array.FindIndex(dateTimes1, a => a == dt); //get the index of the current item in dateTimes1
int dt2index = Array.FindIndex(dateTimes2, x => x.Hour == currentHour); //get the index of the item in dateTimes2 matching dateTimes1 hour field
if (currentHour > lastHour)
{
Console.WriteLine("{0}, {1}", dt1index, dateTimes2.GetUpperBound(0));
}
else
{
Console.WriteLine("{0}, {1}", dt1index, dt2index);
}
}
This simply looks at each of the values in dateTimes1 and dateTimes2 and returns the first match it finds (very similar to your loop).
To determine dt1index, we look through dateTimes1 and return the first match where a => a == dt (a is just the predicate, representing the "current" value in dateTimes1 - think of i = 0,1,2,etc in a regular loop ).
Similarly, to determine dt2index, we look for the first match on x => x.Hour == dt.Hour -- that is, where the "current" dt's hour field matches the hour field in dateTimes2.
In both cases, the first match is returned - if no matches are found, -1 is returned.
When we go to write to the console, we check if currentHour is greater than the last hour in dateTimes2 if so, we just write the current index of dateTimes1 and the last index of dateTimes2. Otherwise, we write the current index of dateTimes1 and the index where the hour matches on dateTimes2.
Using Linq:
var hour = new TimeSpan(1, 0, 0);
var dt2MaxValue = dateTimes2.Max();
for (int i = 0; i < dateTimes1.Length; i++)
{
var output = string.Format("{0}, {1}",
i,
dateTimes2
.Select((o, index) => new { index = index, value = o })
.Where(dt2 => (dateTimes1[i] - dt2.value) < hour
|| dt2.value == dt2MaxValue)
.Select(dt2 => dt2.index)
.FirstOrDefault());
Console.WriteLine(output);
}
What the above Linq statement does:
The first Select uses that method's overload which also passes the index of the item. This simply allows that info to cascade through. It uses an anonymous object with both index and the collection item being the index and value properties, respectively.
The Where clause queries the collection of these anonymous objects and compares their value with dateTime1[i]. It gets the one where value is less than dateTime1[i] but not by more than 1 hour, OR if it is the maximum value in the whole collection.
The second Select simply gets the indexes of the items that Where filtered through.
And FirstOrDefault returns just that (ie, the first or default, which is the index of the item selected or 0 if no item was selected).
Related
Suppose I have the following array (my sequences are all sorted in ascending order, and contain positive integers)
var tabSequence = new[] { 1, 2, 3, 7, 8, 9, 12, 15, 16, 17, 22, 23, 32 };
I made a code using LINQ and a loop to search missing numbers like that :
List<Int32> lstSearch = new List<int>();
var lstGroup = tabSequence
.Select((val, ind) => new { val, group = val - ind })
.GroupBy(v => v.group, v => v.val)
.Select(group => new{ GroupNumber = group.Key, Min = group.Min(), Max = group.Max() }).ToList();
for (int number = 0; number < lstGroup.Count; number++)
{
if (number < lstGroup.Count-1)
{
for (int missingNumber = lstGroup[number].Max+1; missingNumber < lstGroup[number+1].Min; missingNumber++)
lstSearch.Add(missingNumber);
}
}
var tabSequence2 = lstSearch.ToArray();
// Same result as var tabSequence2 = new[] {4, 5, 6, 10, 11, 13, 14, 18, 19, 20, 21, 24, 25, 26, 27, 28, 29, 30, 31 };
This code works but i'd like to know if there a better way to do the same thing only with linq.
Maybe I am just not understanding the problem. Your code seems very complicated, you could make this a lot simpler:
int[] tabSequence = new[] { 1, 2, 3, 7, 8, 9, 12, 15, 16, 17, 22, 23, 32 };
var results = Enumerable.Range(1, tabSequence.Max()).Except(tabSequence);
//results is: 4, 5, 6, 10, 11, 13, 14, 18, 19, 20, 21, 24, 25, 26, 27, 28, 29, 30, 31
I made a fiddle here
You can use IEnumerable.Aggregate to your advantage. The overload I choose uses an accumulator seed (empty List<IEnumerable<int>>) and proceeds to iterate over each item in your array.
The first time I set an lastNR defined before using the aggregate to the firsst number we iterate over. We compare the nexts iterations actual nr against this lastNr.
If we are in sequence we just increment the lastNr.
If not, we generate the missing numbers via Enumerable.Range(a,count) between lastNr
and the actual nr and add them to our accumulator-List. Then we set the lastNr to nr to continue.
public static List<IEnumerable<int>> GetMissingSeq(int[] seq)
{
var lastNr = int.MinValue;
var missing = seq.Aggregate(
new List<IEnumerable<int>>(),
(acc, nr) =>
{
if (lastNr == int.MinValue || lastNr == nr - 1)
{
lastNr = nr; // first ever or in sequence
return acc; // noting to do
}
// not in sequence, add the missing into our ac'umulator list
acc.Add(Enumerable.Range(lastNr + 1, nr - lastNr - 1));
lastNr = nr; //thats the new lastNR to compare against in the next iteration
return acc;
}
);
return missing;
}
Tested by:
public static void Main(string[] args)
{
var tabSequence = new[] { 1, 2, 3, 7, 8, 9, 12, 15, 16, 17, 22, 23, 32 };
var lastNr = int.MinValue;
var missing = tabSequence.Aggregate(
new List<IEnumerable<int>>(),
(acc, nr) =>
{
if (lastNr == int.MinValue || lastNr == nr - 1)
{
lastNr = nr; // first ever or in sequence
return acc; // noting to do
}
acc.Add(Enumerable.Range(lastNr + 1, nr - lastNr - 1));
return acc;
}
);
Console.WriteLine(string.Join(", ", tabSequence));
foreach (var inner in GetMissingSeq(tabSequence))
Console.WriteLine(string.Join(", ", inner));
Console.ReadLine();
}
Output:
1, 2, 3, 7, 8, 9, 12, 15, 16, 17, 22, 23, 32 // original followed by missing sequences
4, 5, 6
10, 11
13, 14
18, 19, 20, 21
24, 25, 26, 27, 28, 29, 30, 31
If you are not interested in the subsequences you can use GetMissingSeq(tabSequence).SelectMany(i => i) to flatten them into one IEnumerable.
Given I have a list of KeyValuePair<DateTime,DateTime> (which represent date ranges available), and I have another list of same type (which represent date ranges not available), then I need to end up with a list of date ranges that are within the first list's ranges, but with ranges in first list being sliced by ranges in the second list. It may be that second list only has one entry, but it covers the entire available range, in which case the result should be empty.
To illustrate:
available: |~| |~~~~| |~~| |~~~~~~~~~~~~~~~~~~~~~|
unavailab: |~~~~~~~~~| |~| |~~~~~~| |~~~~~~~~|
expected : |~| |~| |~~~~~~~| |~~|
Or, in code, if you prefer (not same as illustration):
IList<KeyValuePair<DateTime, DateTime>> scheduleDates = new List<KeyValuePair<DateTime, DateTime>>()
{
new KeyValuePair<DateTime, DateTime>(new DateTime(2016, 11, 15, 9, 0, 0), new DateTime(2016, 11, 15, 12, 0, 0)),
new KeyValuePair<DateTime, DateTime>(new DateTime(2016, 11, 15, 15, 0, 0), new DateTime(2016, 11, 15, 21, 0, 0))
};
IList<KeyValuePair<DateTime, DateTime>> holidayDates = new List<KeyValuePair<DateTime, DateTime>>()
{
new KeyValuePair<DateTime, DateTime>(new DateTime(2016, 11, 15, 11, 0, 0), new DateTime(2016, 11, 15, 16, 0, 0)),
new KeyValuePair<DateTime, DateTime>(new DateTime(2016, 11, 15, 17, 0, 0), new DateTime(2016, 11, 15, 18, 0, 0)),
new KeyValuePair<DateTime, DateTime>(new DateTime(2016, 11, 15, 20, 0, 0), new DateTime(2016, 11, 15, 24, 0, 0)),
};
IList<KeyValuePair<DateTime, DateTime>> availability = new List<KeyValuePair<DateTime, DateTime>>()
{
new KeyValuePair<DateTime, DateTime>(new DateTime(2016, 11, 15, 9, 0, 0), new DateTime(2016, 11, 15, 11, 0, 0)),
new KeyValuePair<DateTime, DateTime>(new DateTime(2016, 11, 15, 16, 0, 0), new DateTime(2016, 11, 15, 17, 0, 0)),
new KeyValuePair<DateTime, DateTime>(new DateTime(2016, 11, 15, 18, 0, 0), new DateTime(2016, 11, 15, 20, 0, 0))
};
What would be the most efficient method of doing this? I can only think of a brute force, and surely that's not the way to go. Note that both input lists and/or the output list could be empty.
I have an array of MyClass, which can be simplified as follow:
public class MyClass {
public int Id;
public string Origin;
public int Points;
public DateTime RequestTime;
public MyClass(int id, string origin, int points, DateTime requestTime) {
Id = id;
Origin = origin;
Points = points;
RequestTime = requestTime;
}
}
Now, in the Array, without any errors from the user side or throughout the input process, there cannot be MyClass instance with identical Id and Origin.
However, if there be any, I should resolve it. And here are the resolving rules:
Firstly by Points - that is, to take one among the duplicates which has the highest Points
But if the Points are the same, I have to further resolve it by using RequestTime - the latest will be taken.
And if, there is no difference in RequestTime, then I can take one of the duplicates arbitrarily.
Here is the sample data input I have:
MyClass[] myarr = new MyClass[] {
new MyClass(1, "Ware House 1", 5, new DateTime(2016, 1, 26, 14, 0, 0)), //[0]
new MyClass(1, "Ware House 1", 7, new DateTime(2016, 1, 26, 14, 0, 0)), //[1] //higher points
new MyClass(1, "Ware House 2", 7, new DateTime(2016, 1, 26, 14, 0, 0)), //[2]
new MyClass(1, "Ware House 2", 7, new DateTime(2016, 1, 26, 14, 1, 0)), //[3] //later time
new MyClass(1, "Ware House 2", 7, new DateTime(2016, 1, 26, 14, 0, 0)), //[4]
new MyClass(2, "Ware House 2", 7, new DateTime(2016, 1, 26, 14, 0, 0)), //[5] //higher points
new MyClass(2, "Ware House 2", 5, new DateTime(2016, 1, 26, 14, 1, 0)), //[6] //later time but less points
new MyClass(3, "Ware House 1", 6, new DateTime(2016, 1, 26, 14, 0, 0)), //[7] //triplet, pick any
new MyClass(3, "Ware House 1", 6, new DateTime(2016, 1, 26, 14, 0, 0)), //[8] //triplet, pick any
new MyClass(3, "Ware House 1", 6, new DateTime(2016, 1, 26, 14, 0, 0)) //[9] //triplet, pick any
};
The final result should be [1], [3], [5], + any of [7]/[8]/[9]
I want to implement LINQ solution for it, but stuck. I do not know how make query it at once.
Any idea?
Group by {Id, Origin} and take the first one from each group when ordered by Points and RequestTime:
var query = from x in myarr
group x by new {x.Id, x.Origin}
into g
select (
from z in g
orderby z.Points descending, z.RequestTime descending
select z).First();
In method syntax, this is:
var query =
myarr.GroupBy(x => new {x.Id, x.Origin})
.Select(g => g.OrderByDescending(z => z.Points)
.ThenByDescending(z => z.RequestTime)
.First());
Try following:
myarr.OrderBy(m=> m.Points).ToList();
or
myarr.OrderBy(m=> m.Points).Orderby(m=> m.RequestTime);
I am trying to write an add-in for a finite element geology piece of software.
I have three arrays which are essentially a coordinate system. I want to assign a value to a variable depending on its position in the grid system. Basically I want to say if my node is within an x range and a y range, then my aquifer thickness at this node is this value. So far I have this.
//create an array of xcoords of data points:
double[] xcoord = new double[11] {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50};
//create an array of ycoords of data points:
double[] ycoord = new double[11] {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50};
//create an array of aquifer thickness
double[] aquiferThicknessPoints = new double[121]
{
10, 10, 12, 13, 12, 15, 14, 15, 14, 13, 13,
10, 10, 13, 15, 16, 14, 13, 15, 16, 12, 13,
12, 14, 15, 18, 19, 17, 14, 15, 18, 14, 14,
13, 14, 15, 18, 20, 17, 15, 17, 18, 15, 15,
14, 15, 17, 18, 21, 17, 18, 18, 19, 17, 16,
15, 15, 17, 17, 20, 21, 21, 19, 19, 18, 18,
15, 15, 17, 20, 20, 21, 22, 21, 19, 19, 19,
16, 17, 19, 20, 22, 23, 22, 21, 20, 20, 20,
17, 18, 20, 22, 23, 24, 24, 23, 22, 20, 21,
18, 19, 21, 22, 24, 25, 24, 23, 22, 22, 22,
19, 19, 22, 22, 24, 25, 25, 23, 23, 22, 23,
};
dataPointSpacingHalf = dataPointSpacing / 2;
for (int i = 0; i < xcoord.Length; i++)
{
for (int j = 0; j < ycoord.Length; j++)
{
if (nodeX >= (xcoord[i] - dataPointSpacingHalf) && (nodeX < (xcoord[i] + dataPointSpacingHalf)) && (nodeY >= (ycoord[j] - dataPointSpacingHalf) && (nodeY < (ycoord[j] + dataPointSpacingHalf))))
{
aquiferThickness = aquiferThicknessPoints[?];
}
}
}
I can see how the nested for loops will loop through 110 times, but i don't know how to assign my aquifer thickness from my array to each loop.
I open to any way of solving this problem as I'm very new new to programming and am still not sure which is the best way to achieve things.
Just use i * xcoord.Length + j insted of ?
Here is the code:
for (int i = 0; i < xcoord.Length; i++)
{
for (int j = 0; j < ycoord.Length; j++)
{
//Here is the magic!
//without considering coordinates
//aquiferThickness[i, j] = aquiferThicknessPoints[i * xcoord.Length + j];
//considering coordinates
aquiferThickness[i, j] =
aquiferThicknessPoints[
CoordToIndex(xNode,indexedCoords) * xcoord.Length +
CoordToIndex(yNode,indexedCoords)];
}
}
Also to consider the xNode, yNode coordinate, you can take this approach
Dictionary<int, double> indexedCoords = new Dictionary<int, double> { { 0, 0 }, { 1, 5 }, { 2, 10 }, .... };
int CoordToIndex(double node, Dictionary<int, double> indexedCoords)
{
return indexedCoords.First(i => i.Value > node).Key;
}
You want to use a two-dimensional array for your aquiferThicknessPoints:
double[,] aquiferThicknessPoints = new double[,]
{
{10, 10, 12, 13, 12, 15, 14, 15, 14, 13, 13},
{10, 10, 13, 15, 16, 14, 13, 15, 16, 12, 13},
{12, 14, 15, 18, 19, 17, 14, 15, 18, 14, 14},
{13, 14, 15, 18, 20, 17, 15, 17, 18, 15, 15},
// the rest
};
You can then address the data using the two coordinates:
aquiferThickness = aquiferThicknessPoints[j, i];
(or i, j, it's not obvious how your data is organized)
I'm expecting the following sequence of time data in 30 minute interval from another system. I need to make this 30 minute pair in to one hour interval:
e.g.
Source Data:
Start:7:00
End: 7:30
Start:7:30
End: 8:00
Start:8:00
End: 8:30
Start:8:30
End: 9:00
Convert in to hour data like below:
Start:7:00
End: 8:00
Start:8:00
End: 9:00
If there's no match for 30 minute block this record should be ignored:
e.g
Start:7:00
End: 7:30
Start:7:30
End: 8:00
Start:8:00
End: 8:30
Start:8:30
End: 9:00
Start:9:00 <-- Ignore this
End: 9:30 <-- Ignore this
Can someone please suggest how this can be achieved?
The general idea for my solution is that you simply only take elements at indices:
0, 3, 4, 7, 8, 11, 12, ...
Or in other words - if index % 4 is 0 or 3.
Before doing that, we check if there are leftover dates at the end with count % 4.
If there are - we cut them.
This assumes that the dates will be all valid as the input you illustrated.
Here is my implementation:
public static IEnumerable<DateTime> CutTimes(IEnumerable<DateTime> source)
{
var count = source.Count();
var ignore = count % 4;
if (ignore != 0)
{
source = source.Take(count - ignore);
}
return source.Where((time, index) => index % 4 == 0 || index % 4 == 3);
}
Usage:
var times = new[]
{
new DateTime(2012, 5, 10, 7, 00, 0),
new DateTime(2012, 5, 10, 7, 30, 0),
new DateTime(2012, 5, 10, 7, 30, 0),
new DateTime(2012, 5, 10, 8, 00, 0),
new DateTime(2012, 5, 10, 8, 00, 0),
new DateTime(2012, 5, 10, 8, 30, 0),
new DateTime(2012, 5, 10, 8, 30, 0),
new DateTime(2012, 5, 10, 9, 00, 0),
new DateTime(2012, 5, 10, 9, 00, 0),
new DateTime(2012, 5, 10, 9, 30, 0)
};
var result = CutTimes(times);
foreach (var time in result)
{
Console.WriteLine(time);
}
Output:
10/05/2012 07:00:00
10/05/2012 08:00:00
10/05/2012 08:00:00
10/05/2012 09:00:00