I have List list= [1,2,3,4,4,4,9,6,7,7,7,8,8,8,8,8,8,8];
how can i return 8 as max repeated value
Something like this?
void main() {
final list = [1, 2, 3, 4, 4, 4, 9, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8];
print(findMaxDuplicatedElementInList(list)); // 8
}
T findMaxDuplicatedElementInList<T>(Iterable<T> list) => list
.fold<Map<T, int>>(
{},
(map, element) =>
map..update(element, (value) => value + 1, ifAbsent: () => 1))
.entries
.reduce((e1, e2) => e1.value > e2.value ? e1 : e2)
.key;
I'd just write it out, as straight-forward as possible:
Assuming the equal elements are always adjacent, and list cannot be empty, return arbitrary element with maximal count if there is more than one:
T maxDuplicated<T>(List<T> elements) {
var element = elements.count;
var count = 1;
var maxElement = element;
var maxCount = count;
for (var i = 1; i < elements.length; i++) {
var nextElement = elements[i];
if (element != nextElement) {
element = nextElement;
count = 1;
} else {
count += 1;
if (count > maxCount) {
maxElement = element;
maxCount = count;
}
}
}
return maxElement;
}
Assuming elements come in random order, so we need to remember every element we have seen,
still not allowing an empty list as input:
T maxDuplicated<T>(List<T> elements) {
var maxCount = 1;
var maxElement = elements.first
var seen = <T, int>{maxElement: maxCount};
for (var i = 1; i < elements.length; i++) {
var element = elements[i];
var count = seen[element] = (seen[element] ?? 0) + 1;
if (count > maxCount) {
maxCount = count;
maxElement = element;
}
}
return maxElement;
}
(Alternatively, I'd sort the list first, if allowed, to always be in the former situation. It's not faster than using a map, if we assume hash map lookup to be a constant time operation, but it will be more memory efficient.)
Related
Problem: The details of item can't move together when latest stock update.
The picture below shows the initial input:
The picture below shows the problem when the stock changes:
Desired Output:
The formula of Name column for repeat the item:
=QUERY(ArrayFormula(FLATTEN(SPLIT(REPT($AL11:$AL13&"#",$AM11:$AM13),"#"))),"where Col1 is not null")
Suggestion
Using apps script
Not sure if it is possible using formula, but here's a script you can use. This of course still needs some tweaking to align with your spreadsheet.
Also what I've done with the script is it deletes both the last name and detail if you change it to lower stock since it makes more sense rather than keeping it in same spot.
try:
function onEdit(e) {
const ss = e.source;
const range = e.range;
if(range.getColumn() == 2 && range.getRow() > 1) {
const sheet = ss.getActiveSheet();
const lastRow = sheet.getLastRow();
const dataRange = sheet.getRange(2, 1, lastRow - 1, 2);
const dataValues = removeEmptyRows(dataRange.getValues());
const outputRange = dataRange.offset(0, 2);
const outputData = removeEmptyRows(outputRange.getValues());
let output = [];
dataValues.filter(x => x[1] && x[1] > 0).forEach(row => {
const [fruit, count] = row;
let currentFruit = outputData.filter(x => x[0] == fruit);
let currentCount = currentFruit.length, rows;
if(currentCount < count) {
rows = count - currentCount;
currentFruit.push(...Array(rows).fill([fruit, '']));
}
else if(count < currentCount) {
rows = currentCount - count;
currentFruit = currentFruit.slice(0, -1 * rows);
}
output.push(...currentFruit);
});
outputRange.clearContent();
sheet.getRange(2, 3, output.length, output[0].length).setValues(output);
}
}
function removeEmptyRows(array) {
return array.filter(x => x.filter(String).length > 0)
}
Result:
Let me know if this works or if you have other questions.
EDIT - Modified code
function onEdit(e) {
const ss = e.source;
const range = e.range;
if(range.getColumn() == 2 && range.getRow() > 1) {
// Needed values to modify
// Input: Data that holds how many items are in each category
const inputRowStart = 2; // Input starts at:
const inputColStart = 1; // ROW 2, COL 1 (A2)
// Output: Data where the output is written
const outputRowStart = 2; // Output starts at:
const outputColStart = 3; // ROW 2, COL 3 (C2)
const outputCols = 3; // 3 COLS (Fruits, Details, Date)
const sheet = ss.getActiveSheet();
const lastRow = sheet.getLastRow();
const inputRange = sheet.getRange(inputRowStart, inputColStart, lastRow - 1, 2);
const inputValues = removeEmptyRows(inputRange.getValues());
const outputRange = sheet.getRange(outputRowStart, outputColStart, lastRow - 1, outputCols);
const outputData = removeEmptyRows(outputRange.getValues());
let output = [];
inputValues.filter(row => row[1] > 0).forEach(row => {
const [fruit, count] = row;
let currentFruit = outputData.filter(row => row[0] == fruit);
let currentCount = currentFruit.length, rows;
if(currentCount < count) {
rows = count - currentCount;
currentFruit.push(...Array(rows).fill([fruit, ...Array(outputCols - 1).fill('')]));
}
else if(count < currentCount) {
currentFruit = currentFruit.slice(0, count);
}
output.push(...currentFruit);
});
outputRange.clearContent();
sheet.getRange(outputRowStart, outputColStart, output.length, output[0].length).setValues(output);
}
}
function removeEmptyRows(array) {
return array.filter(row => row.filter(String).length > 0)
}
I'm really confused on how I'm gonna find the index of item in array where there's a lot of duplicated words.
List<String> _words = "the quick brown fox jumps over the lazy dog".split(" ");
now I want to get the all the index in word "the" programmatically.
I expect a result of
List indexOfWords = _words.indexOfAll("the");
print(indexOfWords);
// [0, 6]
You can define indexOfAll as an extension method. I would implement it like this:
extension ListExtension<T> on List<T> {
List<int> indexOfAll(T item) => [
for (int i = 0; i < length; i++)
if (this[i] == item) i,
];
}
You can create an extension method. like this:
extension Occurrences on List {
List<int> indexOfAll(String pattern) {
List<int> indexes = [];
for (int i = 0; i < this.length; i++) {
if (this[i] == pattern) {
indexes.add(i);
}
}
return indexes;
}
}
then you can use it as function on your list
print(_words.indexOfAll("the")); // [0, 6]
I don't know if there is a direct solution for this job. To solve this problem I developed a function called GetIndexes() and it works successfully.
This example prints the following output to the console:
[the, quick, brown, fox, jumps, over, the, lazy, dog]
[3, 9, 15, 19, 25, 30, 34, 39]
The solution I developed is available below:
void main()
{
String text = "the quick brown fox jumps over the lazy dog";
var split = ' ';
List<int> indexes = [];
List<String> words;
words = text.split(split);
GetIndexes(text, split, indexes);
print(words);
print(indexes);
}
void GetIndexes(String text, var split, List<int> indexes)
{
int index = 0;
for(int i=0 ; i<text.length; ++i)
{
if(text[i] == split)
{
indexes.insert(index, i);
++index;
}
}
}
Basically I have two google sheets that look something like this:
a table where people can put in their email and select what kind of foo they are using
email
foo
example#email.com
This Foo
and then another table with information about the foo
foo name
foo type
foo boolean1
foo boolean2
This Foo
String
True
True
That Foo
Number
False
True
Other Foo
String
False
False
In a Separate Sheet I'd like to have a dashboard-like view of things wherein I would have counts of various things like number of people, how many of each type of Foo, etc
Where I'm having trouble is figuring out how to pull things like "Number of people who have selected String foos" and such
like, basically i want the google-query equivalent to (in sql)
SELECT COUNT(p.*) FROM people p JOIN info i on p.foo = i.foo_name GROUP BY i.foo_type WHERE i.foo_type = 'String'
What I would be looking for is a table that looks like this:
Data
Count
Active Roster
4
String
3
Number
1
I have also seen many solutions that have complicated formulas using VLOOKUP, INDEX, MATCH, etc.
I decided to write a user function to combine tables, or as I refer to it, de-normalize the database. I wrote the function DENORMALIZE() to support INNER, LEFT, RIGHT and FULL joins. By nesting function calls one can join unlimited tables in theory.
DENORMALIZE(range1, range2, primaryKey, foreignKey, [joinType])
Parameters:
range1, the main table as a named range, a1Notation or an array
range2, the related table as a named range, a1Notation or an array
primaryKey, the unique identifier for the main table, columns start with "1"
foreignKey, the key in the related table to join to the main table, columns start with "1"
joinType, type of join, "Inner", "Left", "Right", "Full", optional and defaults to "Inner", case insensitive
Returns: results as a two dimensional array
Result Set Example:
=QUERY(denormalize("Employees","Orders",1,3), "SELECT * WHERE Col2 = 'Davolio' AND Col8=2", FALSE)
EmpID
LastName
FirstName
OrderID
CustomerID
EmpID
OrderDate
ShipperID
1
Davolio
Nancy
10285
63
1
8/20/1996
2
1
Davolio
Nancy
10292
81
1
8/28/1996
2
1
Davolio
Nancy
10304
80
1
9/12/1996
2
Other Examples:
=denormalize("Employees","Orders",1,3)
=denormalize("Employees","Orders",1,3,"full")
=QUERY(denormalize("Employees","Orders",1,3,"left"), "SELECT * ", FALSE)
=QUERY(denormalize("Employees","Orders",1,3), "SELECT * WHERE Col2 = 'Davolio'", FALSE)
=QUERY(denormalize("Employees","Orders",1,3), "SELECT * WHERE Col2 = 'Davolio' AND Col8=2", FALSE)
=denormalize("Orders","OrderDetails",1,2)
// multiple joins
=denormalize("Employees",denormalize("Orders","OrderDetails",1,2),1,3)
=QUERY(denormalize("Employees",denormalize("Orders","OrderDetails",1,2),1,3), "SELECT *", FALSE)
=denormalize(denormalize("Employees","Orders",1,3),"OrderDetails",1,2)
=QUERY(denormalize("Employees",denormalize("Orders","OrderDetails",1,2),1,3), "SELECT *", FALSE)
=QUERY(denormalize(denormalize("Employees","Orders",1,3),"OrderDetails",4,2), "SELECT *", FALSE)
function denormalize(range1, range2, primaryKey, foreignKey, joinType) {
var i = 0;
var j = 0;
var index = -1;
var lFound = false;
var aDenorm = [];
var hashtable = [];
var aRange1 = "";
var aRange2 = "";
joinType = DefaultTo(joinType, "INNER").toUpperCase();
// the 6 lines below are used for debugging
//range1 = "Employees";
//range1 = "Employees!A2:C12";
//range2 = "Orders";
//primaryKey = 1;
//foreignKey = 3;
//joinType = "LEFT";
// Sheets starts numbering columns starting with "1", arrays are zero-based
primaryKey -= 1;
foreignKey -= 1;
// check if range is not an array
if (typeof range1 !== 'object') {
// Determine if range is a1Notation and load data into an array
if (range1.indexOf(":") !== -1) {
aRange1 = ss.getRange(range1).getValues();
} else {
aRange1 = ss.getRangeByName(range1).getValues();
}
} else {
aRange1 = range1;
}
if (typeof range2 !== 'object') {
if (range2.indexOf(":") !== -1) {
aRange2 = ss.getRange(range2).getValues();
} else {
aRange2 = ss.getRangeByName(range2).getValues();
}
} else {
aRange2 = range2;
}
// make similar structured temp arrays with NULL elements
var tArray1 = MakeArray(aRange1[0].length);
var tArray2 = MakeArray(aRange2[0].length);
var lenRange1 = aRange1.length;
var lenRange2 = aRange2.length;
hashtable = getHT(aRange1, lenRange1, primaryKey);
for(i = 0; i < lenRange2; i++) {
index = hashtable.indexOf(aRange2[i][foreignKey]);
if (index !== -1) {
aDenorm.push(aRange1[index].concat(aRange2[i]));
}
}
// add left and full no matches
if (joinType == "LEFT" || joinType == "FULL") {
for(i = 0; i < lenRange1; i++) {
//index = aDenorm.indexOf(aRange1[i][primaryKey]);
index = aScan(aDenorm, aRange1[i][primaryKey], primaryKey)
if (index == -1) {
aDenorm.push(aRange1[i].concat(tArray2));
}
}
}
// add right and full no matches
if (joinType == "RIGHT" || joinType == "FULL") {
for(i = 0; i < lenRange2; i++) {
index = aScan(aDenorm, aRange2[i][foreignKey], primaryKey)
if (index == -1) {
aDenorm.push(tArray1.concat(aRange2[i]));
}
}
}
return aDenorm;
}
function getHT(aRange, lenRange, key){
var aHashtable = [];
var i = 0;
for (i=0; i < lenRange; i++ ) {
//aHashtable.push([aRange[i][key], i]);
aHashtable.push(aRange[i][key]);
}
return aHashtable;
}
function MakeArray(length) {
var i = 0;
var retArray = [];
for (i=0; i < length; i++) {
retArray.push("");
}
return retArray;
}
function DefaultTo(valueToCheck, valueToDefault) {
return typeof valueToCheck === "undefined" ? valueToDefault : valueToCheck;
}
// Search a multi-dimensional array for a value
function aScan(aValues, searchStr, searchCol) {
var retval = -1;
var i = 0;
var aLen = aValues.length;
for (i = 0; i < aLen; i++) {
if (aValues[i][searchCol] == searchStr) {
retval = i;
break;
}
}
return retval;
}
You can make a copy of the google sheet with data and examples here:
https://docs.google.com/spreadsheets/d/1vziuF8gQcsOxTLEtlcU2cgTAYL1eIaaMTAoIrAS7mnE/edit?usp=sharing
I need to get all possible subsets of an array.
Say I have this:
<int>[1, 2, 3]
How do I get this?
[], [1], [2], [3],[1, 2], [2, 3], [1, 3], [1, 2, 3]
I am interested in all subsets. For subsets of specific length, refer to the following questions:
How to find all subsets of a set in JavaScript?
Here is my take on it, with only native function as in your link:
List getAllSubsets(List l) => l.fold<List>([[]], (subLists, element) {
return subLists
.map((subList) => [
subList,
subList + [element]
])
.expand((element) => element)
.toList();
});
If you want a specific size:
List getSizedSubsets(List l, int size) =>
getAllSubsets(l).where((element) => element.length == size).toList();
I'd probably go with something simple like:
Iterable<Set<E>> subsets<E>(Set<E> elements) sync* {
if (elements.length >= 32) {
// Otherwise there'll be more than 2^32 subsets. And bitops will overflow in JS.
throw ArgumentError.value(elements, "elements", "must have less than 32 elements");
}
var list = [...elements];
var subsetCount = 1 << list.length;
for (var i = 0; i < subsetCount; i++) {
yield {
for (var j = 0, bit = 1; j < elements.length; j++, bit <<= 1)
if (i & bit != 0) list[j]
};
}
}
Another approach is to only have one set, and then update it iteratively to contain different elements. It's possible to go through all the sets doing only single-element changes on each step (using Gray-code):
/// Iterates a set through all combinations of its elements.
///
/// Adds and removes elements from [set] to make it iterate through all
/// possible combinations of its initial elements.
/// The current value of the iterator is always [set].
/// If iterated through to the end, the [set] ends up with all its original elements.
Iterable<Set<E>> subsets<E>(Set<E> set) sync* {
if (set.length >= 32) {
throw ArgumentError.value(set, "set", "must have less than 32 elements");
}
var list = [...set];
var prev = 0;
var counter = 0;
do {
yield set;
var next = ++counter ^ (counter >> 1);
var bit = prev ^ next; // One bit set.
var index = bit.bitLength - 1;
if (index >= list.length) index = 0;
var element = list[index];
if (next & bit == 0) {
set.add(element);
} else {
set.remove(element);
}
prev = next;
} while (set.length < list.length);
}
In a list of sequential integers, is there a simple way to locate where another integer would be placed (between two of the list members)?
main() {
var myList = new List();
myList.addAll([0, 4, 10, 20, 33, 45, 55, 64]);
int setStart;
int currentPosition;
currentPosition = 12;
// if currentPosition is greater than or equal to myList[fooPosition]
// but less than myList[barPosition]
// setStart = myList[foo]
}
So since the currentPosition is 12, the correct answer for setStart would be 10.
Try checking out package:collection's binarySearch.
Ok, figured it out myself. Pretty simple really. I just needed to add another variable (x) to indicate the list position of the upper number:
for (var i = 0; i < myList.length; i++) {
var x = i + 1;
if (currentPosition >= myList[i] && currentPosition < myList [x]) {
setStart = myList[i];
};
};