Select entire row from 2d object[] where first column value matches - linq

I have a 2D object array containing something like this:
Filename | "filename.txt" | 1
Path | "C:\temp\" | 2
Date | 16/06/2015 | 3
I'd like to extract from this array the row which matches a given header, where the header is the value of the first column. For example, given 'Path', I'd like to be given:
Path | "C:\temp\" | 2
I'm currently looping through to find the correct row number, then looping through that row to extract it. I'm pretty sure that using linq could make this look a bit nicer but I've had very little use in it.
Any help would be greatly appreciated.

If you are using a jagged array, it's fairly straightforward:
object[][] jagged = { new object[] {"Filename", "filename.txt", 1},
new object[] {"Path", #"C:\Temp", 2},
new object[] {"Date", new DateTime(2015,6,16), 3}};
var q = from j in jagged
where (j[0] as string) == "Path"
select j;
var row = q.FirstOrDefault();
// row is an object[3] array
in fact, that can be reduced to just:
var row = jagged.FirstOrDefault(j =>(j[0] as string) == "Path");
Howeverf, if you are using a rectangluar array, it get much uglier. Rectangular arrays have no concept of "rows" or "columns", just cells, so you'd need to build a new array:
object[,] rect = { { "Filename", "filename.txt", 1},
{"Path", #"C:\Temp", 2},
{"Date", new DateTime(2015,6,16), 3}};
var result = new object[rect.GetLength(1)];
for(int i = rect.GetLowerBound(0); i <= rect.GetUpperBound(0); ++i)
{
if (rect[i, rect.GetLowerBound(1)] as string == "Path")
{
for (int j = 0; j < rect.GetLength(1); ++j)
result[j] = rect[i, rect.GetLowerBound(1) + j];
break;
}
}

Related

Sorting Data Range Based on Arbitrarily Ordered String Key Through Google Scripts

I am trying to sort values of column A depending on the values at column B ordered by matching keys at column E expected results are in column C
I can achieve this through formula described here
=SORT(A2:A;ARRAYFORMULA(MATCH(B2:B;E2:E;0));1)
I am seeking a google script answer to enhance its automation by using it through onEdit trigger. I can sort column A according to column B alphabetically through this pseudocode snippet. I need help on attaching this the numeric order of sort key
var sortRange = sheet.getRange(2,1,lastRow,lastCol)
sortRange.sort({column: 2, ascending: false});
Thanks in advance
====== Update ======
Found a Solution Thanks to ziganotschka
// Retrieve Keys&Range
var sortKeys = ss.getRange(2, 5, 3, 1).getValues().flat();
var sortRange = ss.getRange(2,1,lastRow,2).getValues();
// Set an index to each row according to its relation to the search keys
for (var ii=0; ii < sortRange.length; ii++){
var index = sortKeys.indexOf(sortRange[ii][1]);
sortRange[ii].push(index);
}
// Sort the array ascending by index
activeCell.setValue(sortRange[0][2]);
sortRange.sort(function(a,b) {
var varA = a[2];
var varB = b[2];
return varA == varB ? 0 : varA > varB ? -1 : 1;
});
// Reduce the array to the sorted values of Column A
var sortedColumn = sortRange.map(function(i){return [i[0]];});
//set values into column C
ss.getRange(2, 6, sortedColumn.length, 1).setValues(sortedColumn);
If you want to sort by script instead of formula, you have to do it differently
sortRange.sort will sort you current spreadsheet contents rather than outputting the sorted version in a new column
Instead you need to retrieve the values as an array and perform a Javascript array sorting function on it: Array.prototype.sort()
Also, if you want to sort by searchkeys in column E, rather than alphabetically / numerically, you need to append to the retrieved array an index column based on a row's relation tot he search key
The index can be retrieved with Array.prototype.indexOf()
After you sort the three column array (column A, column B and the new index column), reduce the array to the sorted values column and set it into column C with setValues(values)
Sample:
function myFunction() {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var lastRow = sheet.getLastRow();
var lastCol = 2;
var sortRange = sheet.getRange(2,1,lastRow-1,lastCol).getValues();
//retrieve the keys in column E
var keys = sheet.getRange(2, 5, lastRow, 1).getValues().flat();
for (var i =0; i < sortRange.length; i++){
//set an index to each row according to its relation to the search keys
var index = keys.indexOf(sortRange[i][1]);
sortRange[i].push(index);
}
//sort the array ascending by index
sortRange.sort(function(a,b) {
return a[2] - b[2]
});
//reduce the array to the sorted values of Column A
var sortedColumn = sortRange.map(function (i){return [i[0]];});
//set values into column C
sheet.getRange(2, 3, sortedColumn.length, 1).setValues(sortedColumn);
}

Simple nested for loop problems

I am very new to programming and I am trying to create a short macro on google sheets. I want to copy and paste a list of cells a certain number of times based on variables see here. On the first column you have a list of locations and the second column the number of times they should be pasted into a 3rd column (column F). For example i would like to paste A4 fives times into column F4. Then A5 twice into F8 (F4 + 5 rows) and so one until the end of my list.
I came up with the below code but it currently copies each locations of my list 5 times on the same rows (F4-F8) then repeat the same process on the following 5 rows, and so on. I think the issue is with the order of the loops but i can't figure it out really.
Another problem i have is that It only copies each location 5 times (B1), but i am not sure how to make my variable numShift an array for (B4-B16) so that each locations is copied the correct number of times
Any help would be really appreciated !
function myFunction() {
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet()
var activeSheet = ss.getActiveSheet()
var numShift = activeSheet.getRange(4,2).getValue()
for (var k=0;k<65;k=k+5){
for (var indexNumLocation=0;indexNumLocation<15;indexNumLocation++){
for (var indexNumShift=0;indexNumShift<numShift;indexNumShift++)
{var locationRange = activeSheet.getRange(4+indexNumLocation,1).getValue();
activeSheet.getRange(4+k+indexNumShift,6).setValue(locationRange);
}
}
}
}
Try this
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastRow = sheet.getLastRow();
var data = [];
var i, j;
// get times count for each row from B4 to last row
var times = sheet.getRange(4, 2, lastRow - 4, 1).getValues();
// get the data that needs to be copied for each row from A4 to last row
var values = sheet.getRange(4, 1, lastRow - 4, 1).getValues();
// loop over each row
for (i = 4; i <= lastRow; i++) {
// run loop number of times for that row
for (j = 1; j <= times[i]; j++) {
// push the value that needs to be copied in a temporary array
data.push([values[i][0]]);
}
}
// finally put that array data in sheet starting from F4
sheet.getRange(4, 6, data.length, 1).setValues(data);
}

Getting wrong answer when trying to get k subsets from Array in ActionScript

I'm working on a Texas Holdem game and i need to generate all possible k subsets from an Array of cards (represented as numbers in this example). This is how it looks so far:
public function getKSubsetsFromArray(arr:Array, k:int):Array {
var data:Array = new Array();
var result:Array = new Array();
combinations(arr, data, 0, arr.length - 1, 0, k, result, 0);
return result;
}
public function combinations(arr:Array, data:Array, start:int, end:int, index:int, r:int, resultArray:Array, resultIndex:int):int {
if (index == r) {
trace(resultIndex, data);
resultArray[resultIndex] = data;
return ++resultIndex;
}
for (var i:int = start; i<=end && end-i+1 >= r-index; i++) {
data[index] = arr[i];
resultIndex = combinations(arr, data, i + 1, end, index + 1, r, resultArray, resultIndex);
}
return resultIndex;
}
I am new to Actionscript, my idea is to have a function that takes an array of number and a parameter k, and returns an Array of arrays each of size k. However once i test the functions I get an array containing only the last combination nCk times. For example:
var testArray:Array = new Array(1, 2, 3, 4, 5);
trace(getKSubsetsFromArray(testArray, 3));
Returns:
0 1,2,3
1 1,2,4
2 1,2,5
3 1,3,4
4 1,3,5
5 1,4,5
6 2,3,4
7 2,3,5
8 2,4,5
9 3,4,5
The function output is
3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5
Of course it should print an array containing all the combinations listed before but it only prints the last one the right amount of times.
Thank your for your help.
The reason for the error is that when you are making array of arrays you are actually using the reference of the same array (data) so when the last combination is executed the contains of data array become 3,4,5 and each of index of resultArray points to data array so it prints out same values.
Solution :-
if (index == r) {
trace(resultIndex, data);
var result = new Array();
copy(result,data)
resultArray[resultIndex] = result;
return ++resultIndex;
}
Note :-
The above is pseudo code as i am not familiar with actionscript but you can implement copy function that copies values of data into result in actionscript syntax.

Ranking in Linq

There's a generic list of numbers, say
{980, 850,700, 680}---n nos.
I try to compare the above list with a decimal no. say 690., the O/p I need is,to get the ranking of the number which I'm gonna input("692). i,e the desired O/P should be Ranking ="4"
How can I get the O/p for above scenario..??
Following on from Alex's post I think you are looking for
var numbers = new List<int>() { 980, 850, 700, 680 };
var dec = new Decimal(692.0);
var temp = numbers.Count(x => x > dec) + 1;
this will return the position you are looking for
If you want to look for an exact match of a decimal input to a int on the list,you can use FindIndex.
var numbers = new List<int>() { 980, 850, 700, 680 };
var dec = new Decimal(680.0);
var res = numbers.FindIndex(x => x == dec);
It returns the 0-based position of the match.
Your question is not clear, i'm not sure what role 690 is playing.
Assuming that the user can ernter a number and you want to find the rank(index) of the number in the list when it would be inserted. Assuming also that your list should be sorted descending since you want the position of the new int according to it's value:
var input = 692;
var numbers = new List<int>() { 980, 850, 700, 680 };
var firstLower = numbers.OrderByDescending(i => i)
.Select((i, index) => new { Value = i, Index = index })
.FirstOrDefault(x => x.Value < input);
var rank = firstLower == null ? numbers.Count + 1 : firstLower.Index + 1;
Note that the OrderByDescending might be redundant if your list is already sorted, but i assume that your sample data is only sorted accidentally.

Using LINQ to join [n] collections and find matches

Using LINQ I can find matching elements between two collections like this:
var alpha = new List<int>() { 1, 2, 3, 4, 5 };
var beta = new List<int>() { 1, 3, 5 };
return (from a in alpha
join b in beta on a equals b
select a);
I can increased this to three collections, like so:
var alpha = new List<int>() { 1, 2, 3, 4, 5 };
var beta = new List<int>() { 1, 3, 5 };
var gamma = new List<int>() { 3 };
return (from a in alpha
join b in beta on a equals b
join g in gamma on a equals g
select a);
But how can I construct a LINQ query that will return the matches between N number of collections?
I'm thinking if each collection was added to a parent collection, then the parent collection was iterated through using a recursive loop, it may work?
There's no need to recurse - you can just iterate. However, you may find it best to create a set and intersect that each time:
List<List<int>> collections = ...;
HashSet<int> values = new HashSet<int>(collections[0]);
foreach (var collection in collections.Skip(1)) // Already done the first
{
values.IntersectWith(collection);
}
(Like BrokenGlass, I'm assuming you've got distint values, and that you really just want to find the values which are in all the collections.)
If you prefer the immutable and lazy approach, you could use:
List<List<int>> collections = ...;
IEnumerable<int> values = collections[0];
foreach (var collection in collections.Skip(1)) // Already done the first
{
values = values.Intersect(collection);
}
If you have only unique values you can use Intersect:
var result = alpha.Intersect(beta).Intersect(gamma).ToList();
If you need to preserve multiple values that are not unique you can just exclude non-intersecting items from the original collection as an additional step:
alpha = alpha.Where(x => result.Contains(x)).ToList();
To generalize the Intersect approach you can just use a loop to do all intersections one by one:
IEnumerable<List<int>> collections = new [] { alpha, beta, gamma };
IEnumerable<int> result = collections.First();
foreach (var item in collections.Skip(1))
{
result = result.Intersect(item);
}
result = result.ToList();

Resources