I'm often confronted with the following situation: I have some data in a table (nested arrays) and need to loop over all items in it.
Requirements:
The first item should be identified and handled separately.
The last item should be identified and handled separately.
If similiar items are sorted they should be identified in groups with the first and last group item identified. (in this case sort/group by gender)
Subtotals of group items and total of all items should be given.
DATA:
Comic characters with firstname, lastname, tvshow, gender
assuming to be ordered by gender, lastname.
Turanga|Leela|Futurama|Female
Marge|Simpson|The Simpsons|Female
Eric|Cartman|South Park|Male
Peter|Griffin|Family Guy|Male
Homer|Simpson|The Simpsons|Male
QUESTION:
How would you code a loop (any language welcome!) that produces the following output?
Indention is not important.
OUTPUT:
First Entry: Turanga Leele (Futurama)
--
First Female: Turanga Leela (Futurama)
Last Female : Marge Simpson (The Simpsons)
--
Total Females: 2
--
First Male: Eric Cartman (South Park)
Peter Griffin (Family Guy)
Last Male : Homer Simpson (The Simpsons)
--
Total Males: 3
--
Last Entry: Homer Simpson (The Simpsons)
--
Total Entries: 5
I picked the world's most verbose language to do this in (Java), but this should do what you want. It requires only a single pass, but needs one row of look ahead. It also requires at least two lines of input although it would be relatively easy to adapt to any number of lines:
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static java.lang.System.out;
public class Loop {
static enum Col { FIRST_NAME, LAST_NAME, TV_SHOW, GENDER }
private static final List<String[]> characters = Arrays.asList(
new String[] {"Turanga", "Leela", "Futurama", "Female"},
new String[] {"Marge", "Simpson", "The Simpsons", "Female"},
new String[] {"Eric", "Cartman", "South Park", "Male"},
new String[] {"Peter", "Griffin", "Family Guy", "Male"},
new String[] {"Homer", "Simpson", "The Simpsons", "Male"});
public static void summarize(List<String[]> character, Col groupBy) {
assert character.size() > 1;
int total = 0;
Iterator<String[]> i = characters.iterator();
String[] row = next(i);
String[] peek = next(i);
String group;
out.print("First Entry:" + format(row));
Map<String, Integer> subTotals = new HashMap<String, Integer>();
do {
group = col(row, groupBy);
out.print("First " + group + ":");
subTotals.put(group, 0);
do {
out.print(format(row));
total = incrementTotals(total, group, subTotals);
row = peek;
peek = next(i);
} while (peek != null && col(peek, groupBy).equals(group));
total = incrementTotals(total, group, subTotals);
out.print("Last " + group + ":" + format(row));
out.println("--");
out.println("Total " + group + "s:" + subTotals.get(group));
out.println("--");
if (peek == null) break;
row = peek;
peek = next(i);
} while(true);
out.print("Last Entry:" + format(row));
out.println("--");
out.println("Total Entries:" + total);
}
private static String[] next(Iterator<String[]> i) {
if (i.hasNext())
return i.next();
return null;
}
private static int incrementTotals(int total, String group,
Map<String, Integer> subTotals) {
total++;
subTotals.put(group, subTotals.get(group) + 1);
return total;
}
private static String format(String[] row) {
return col(row, Col.FIRST_NAME) + " " + col(row, Col.LAST_NAME)
+ "(" + col(row, Col.TV_SHOW) + ")\n";
}
private static String col(String[] row, Col col) {
return row[col.ordinal()];
}
public static void main(String args[]) {
summarize(characters, Col.GENDER);
}
}
Output is:
First Entry:Turanga Leela(Futurama)
First Female:Turanga Leela(Futurama)
Last Female:Marge Simpson(The Simpsons)
--
Total Females:2
--
First Male:Eric Cartman(South Park)
Peter Griffin(Family Guy)
Last Male:Homer Simpson(The Simpsons)
--
Total Males:3
--
Last Entry:Homer Simpson(The Simpsons)
--
Total Entries:5
For interest sake, I decided to see what this would look like in Groovy which is a real language with closures, metaprogramming and some cool list operators. The following code gives more or less the same results, although it requires the list to be in memory and is not particularly efficient. It is however far more readable, and well.. groovier.
enum Col { FIRST_NAME, LAST_NAME, TV_SHOW, GENDER }
def characters = [
[ "Turanga", "Leela", "Futurama", "Female" ],
[ "Marge", "Simpson", "The Simpsons", "Female" ],
[ "Eric", "Cartman", "South Park", "Male" ],
[ "Peter", "Griffin", "Family Guy", "Male" ],
[ "Homer", "Simpson", "The Simpsons", "Male" ]
]
// Use Groovy Metaprogramming to add some methods to the List class
List.metaClass.first = { cl -> if (delegate.size > 0) cl(delegate[0]) }
List.metaClass.middle = { cl ->
if (delegate.size > 2) 1..delegate.size-2.each { i -> cl(delegate[i]) }
}
List.metaClass.last = { cl -> if (delegate.size > 1) return cl(delegate[delegate.size-1]) }
def format(row) {
return row[Col.FIRST_NAME.ordinal()] + " " +
row[Col.LAST_NAME.ordinal()] + " (" +
row[Col.TV_SHOW.ordinal()] + ")"
}
// Loop through and summarize the Characters
characters.first { row ->
println("First Entry: ${format(row)}")
}
def groups = characters.groupBy { row -> row[Col.GENDER.ordinal()] }
groups.each { groupType, group ->
group.first { row -> println("First ${groupType}: ${format(row)}") }
group.middle { row -> println(format(row)) }
group.last { row -> println("Last ${groupType}: ${format(row)}") }
println("--")
println("Total ${groupType}s: ${group.size}")
println("--")
}
characters.last { row ->
println("Last Entry : ${format(row)}")
println("--")
println("Total Items: " + characters.size())
}
Related
I have a snippets of groovy code (inside strategy.groovy file) as shown below in which I want to display the output in the form of desired array.
The content-type Strategy (in crafter) has the following fields:
Title English (title_en_t)
Title French (title_fr_t)
Description English (description_en_t)
Description French (description_fr_t)
Players (players_o)
groovy code (inside strategy.groovy file):
private createStrategyElement(rootStrategy, ctx) {
Element strategy = new DefaultElement("strategy")
strategy.addElement("players_o.first_name_s").text = rootStrategy.players_o.item.component.first_name_s != null ? rootStrategy.players_o.item.component.first_name_s : "" //Line A
strategy.addElement("players_o.last_name_s").text = rootStrategy.players_o.item.component.last_name_s != null ? rootStrategy.players_o.item.component.last_name_s : "" //Line B
return strategy
}
Line A and Line B display the o/p in the following fashion:
current o/p:
players_o.first_name_s: "[John, The]"
players_o.last_name_s: "[Cena, Undertaker]"
PROBLEM STATEMENT:
I am wondering what changes I need to do in the groovy code above at Line A and Line B so that it displays the o/p in this fashion:
DESIRED O/P:
players: [{
"item": [
{
"first_name_s": "John",
"last_name_s": "Cena"
},
{
"first_name_s": "The",
"last_name_s": "Undertaker"
}
]
}]
don't know what is the crafter-cms. and you have not provided the input for your code.
i found DefaultElement and addElement in dom4j library - so i assume it's the one used in crafter-cms
https://dom4j.github.io/javadoc/2.0.1/org/dom4j/tree/DefaultElement.html
you are expecting json in output but dom4j used to work with xml...
however. just a try
private createStrategyElement(rootStrategy, ctx) {
Element strategy = new DefaultElement("strategy")
def players = strategy.addElement('players')
for(i in rootStrategy.players_o.item.component){
def item = players.addElement('item')
item.addElement('first_name_s').text = i.first_name_s
item.addElement('last_name_s').text = i.last_name_s
}
return strategy
}
the following code i used to debug it in standard groovy console:
#Grab(group='org.dom4j', module='dom4j', version='2.1.3')
import org.dom4j.tree.DefaultElement
import org.dom4j.Element
import org.dom4j.io.XMLWriter
import org.dom4j.io.OutputFormat
def rootStrategy=[
players_o:[
item:[
component:[
[
first_name_s:'John',
last_name_s: 'Cena',
],
[
first_name_s:'The',
last_name_s: 'Undertaker'
],
]
]
]
]
private createStrategyElement(rootStrategy, ctx) {
Element strategy = new DefaultElement("strategy")
def players = strategy.addElement('players')
for(i in rootStrategy.players_o.item.component){
def item = players.addElement('item')
item.addElement('first_name_s').text = i.first_name_s
item.addElement('last_name_s').text = i.last_name_s
}
return strategy
}
println new StringWriter().with{w->
new XMLWriter(w, OutputFormat.createPrettyPrint()).write( createStrategyElement(rootStrategy,null) )
w.toString()
}
I create a multi series linechart and fill its dataset dinamically:
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
for (Map.Entry<Long, TreeMap<Integer, Integer>> entrya : mtypes.entrySet()) {
TreeMap<Integer, Integer> atypes = entrya.getValue();
if (atypes.isEmpty())
continue;
Long aid = entrya.getKey();
String name = (aid < 2 ? "Важное" : (aid < 3 ? "Негатив" : "Позитив"));
for (Map.Entry<Integer, Integer> entrym : atypes.entrySet()) {
int m = entrym.getKey();
dataset.addValue(entrym.getValue(), name, Integer.valueOf(m + 1));
}
image = PDFUtil.printLineChart(writer, "", "", "Баллы", dataset , 500, 0, true);
Source array atypes for one series has the form:
{
1={2=4, 3=1},
2={0=1, 3=1, 4=1},
3={0=3, 1=16, 2=31, 3=2, 4=12}
}
All numerical keys in the map sorted Ascending, but on the chart the columns values are unsorted (horizontal axis):
How can I sort it Ascending? This code didn't help:
chart.getCategoryPlot().getDomainAxis().setColumnRenderingOrder(SortOrder.ASCENDING)
can I get the sum of amount if I have an array like the one below?
[{"_id":"5e154cf38c52231ee19f8",
"refunds":[
{"_id":"5e38f10a754fcf3d48015",
"reason":"refund 1",
"amount":50000,
]},
{"_id":"5e1578b48c52231ee19f8",
"refunds":[
{"_id":"5e37e09ef9ea5e3784043",
"reason":"refund 1",
"amount":100000,
{"_id":"5e37e12a02c27c14580a1",
"reason":"refund 2",
"amount":100000,
{"_id":"5e38f02b754fcf3d48015",
"reason":"refund 3",
"amount":50000,
]},
{"_id":"5e1578b48c52231ee19f8",
"refunds":[]
}]
I hope to get res = 300000
This should be relatively straightforward, we create a flat array of all refund objects using flatMap, then we'll use reduce to sum the total amount.
I've added another function, sumRefundAmountsII to workaround JavaScript environments that do not support.flatMap (pre ES2019)
const data = [ { "_id": "5e154cf38c52231ee19f8", "refunds": [ { "_id": "5e38f10a754fcf3d48015", "reason": "refund 1", "amount": 50000 } ] }, { "_id": "5e1578b48c52231ee19f8", "refunds": [ { "_id": "5e37e09ef9ea5e3784043", "reason": "refund 1", "amount": 100000 }, { "_id": "5e37e12a02c27c14580a1", "reason": "refund 2", "amount": 100000 }, { "_id": "5e38f02b754fcf3d48015", "reason": "refund 3", "amount": 50000 } ] }, { "_id": "5e1578b48c52231ee19f8", "refunds": [] }];
function sumRefundAmounts(array) {
// Get an array of all refunds.
const refunds = array.flatMap(obj => obj.refunds);
// Sum amount for each refund.
return refunds.reduce((sum, refund) => {
return sum + refund.amount;
}, 0);
}
console.log("Total amount:", sumRefundAmounts(data));
// Use this function if your JavaScript environment complains about .flatMap
function sumRefundAmountsII(array) {
// Use a work around if we don't have flatMap...
const refunds = [].concat.apply([], array.map(obj => obj.refunds));
// Sum amount for each refund.
return refunds.reduce((sum, refund) => {
return sum + refund.amount;
}, 0);
}
console.log("Total amount (no flat map):", sumRefundAmountsII(data));
Before answering ... I assume 2 things -
1st - you are looking answer in the Java. 2nd - And you can do things much easier way in java List :
Let we make 2 classes Refund & Composite (Containing List)-
Refund Class -
package com.sabre.ticketing.dhs.service.create.domain;
public class Refund {
String _id;
String reason;
int amount;
public Refund(String _id, String reason, int amount) {
this._id = _id;
this.reason = reason;
this.amount = amount;
}
public int getAmount() {
return amount;
}
}
And here is Composite Class as -
package com.sabre.ticketing.dhs.service.create.domain;
import java.util.List;
public class Composite {
String _id;
List<Refund> refunds;
public Composite(String _id, List<Refund> refunds) {
this._id = _id;
this.refunds = refunds;
}
public List<Refund> getRefunds() {
return refunds;
}
}
The calculation technique is in SumCalc class -
package com.sabre.ticketing.dhs.service.create.domain;
import java.util.ArrayList;
import java.util.List;
public class SumCalc {
public static void main(String[] args){
List<Composite> composites = List.of(new Composite("5e154cf38c52231ee19f8", List.of(new Refund("5e38f10a754fcf3d48015", "refund 1", 50000))),
new Composite("5e154cf38c52231ee19f8",
List.of(new Refund("5e37e09ef9ea5e3784043", "refund 1", 100000),
new Refund("5e37e12a02c27c14580a1", "refund 2", 100000),
new Refund("5e38f02b754fcf3d48015", "refund 3", 50000))),
new Composite("5e154cf38c52231ee19f8", new ArrayList<>()));
// Basically get you Json converted into composites list.. for simplicity and dont want to jackson thing i have initialized list as new ..
Integer finalSum = composites.stream()
.map(Composite::getRefunds)
.flatMap(List::stream)
.map(Refund::getAmount)
.reduce((sum, val) -> sum + val)
.orElse(0);
System.out.println("final Sum is " + finalSum);
}
}
Run the SumClac ...
final Sum is 300000
Process finished with exit code 0
I'm trying to compare a string (userInput) against an array of strings (groupA) to check how many of the items in groupA are present in the userInput.
var groupA = ["game of thrones", "star wars", "star trek" ]
var userInput = "My name is oliver i love game of thrones, star wars and star trek."
var count = 0
func checking() -> Int {
for item in groupA {
// alternative: not case sensitive
if userInput.lowercased().range(of:item) != nil {
count + 1
}
}
return count
}
func Printer() {
print(count)
}
Your code is not very well designed, since you're using a lot of globals, but it will work with a few minor changes:
var groupA = ["game of thrones", "star wars", "star trek" ]
var userInput = "My name is oliver i love game of thrones, star wars and star trek."
var count = 0
func checking() -> Int {
for item in groupA {
// alternative: not case sensitive
if userInput.lowercased().range(of:item) != nil {
count += 1 //Make this `count += 1`
}
}
return count
}
func printer() {
print(count)
}
//Remember to call `checking()` and `printer()`
checking()
printer()
Also note that you should name all functions beginning with a lower-case letter, so Printer() should be printer().
Consider this code instead:
import UIKit
var groupA = ["game of thrones", "star wars", "star trek" ]
var userInput = "My name is oliver i love game of thrones, star wars and star trek."
//The `checking` function has been rewritten as `countOccurerences(ofStringArray:inString)`,
//and now takes parameters and returns a value.
func countOccurrences(ofStringArray stringArray: [String], inString string: String) -> Int {
var result = 0
for item in stringArray {
// alternative: not case sensitive
if string.lowercased().range(of:item) != nil {
result += 1
}
}
return result
}
//And the `printer()` function now takes parameter as well.
func printer(_ count: Int) {
print("count = \(count)")
}
//Here is the code to use those 2 functions after refactoring
let count = countOccurrences(ofStringArray: groupA, inString: userInput)
printer(count)
To make the above code work, you just need to change count + 1 in line 18 to count += 1 I've posted the complete code below.
import Cocoa
import Foundation
var groupA = ["game of thrones", "star wars", "star trek" ]
var userInput = "My name is oliver i love game of thrones, star wars and star trek."
var count = 0
func checking() -> Int {
for item in groupA {
// alternative: not case sensitive
if userInput.lowercased().range(of:item) != nil {
count += 1
}
}
return count
}
func Printer() {
print(count)
}
checking()
Printer()
There are many good examples of searching multiple string values in LINQ e.g.
public static Product[] GetProducts(Guid[] prodIDs)
{
return (from p in GetProducts()
where prodIDs.Contains(p.ProductID)
select p).ToArray<Product>();
}
I have a list of Products that I need to match from a customer,
but I dont have an exact match - the Customers List Of Products contains my ProductID - but it is not exact - e.g.
Customer MyCompany
Description Description
Prod1XY Prod1
AProd2B Prod2
XXXProd3 Prod3
I thus cannot filter from the prodIDs [string array] because Prod1 does not contain Prod1XY
and thus cannot use the examples that are available.
How can I effectively change (reverse) the working examples
as to search CustomerProducts where it contains my Product Description please?
So to confirm : this is not a duplicate. The examples use the string[] x
input parameter and then searches:
where x.contains
I need help to get it : myProducts.Contains(x)
another online example modified to show the situation:
static void Main(string[] args) {
var table = new[] {
new { uid = 1 },
new { uid = 2 },
new { uid = 3 },
new { uid = 4 },
new { uid = 5 }
};
var stringarray = new[] { "1", "5", "10" };
var results = from xx in table
where table.Contains(stringarray)
select xx;
foreach (var result in results) {
Console.WriteLine("Result: " + result.uid.ToString());
}
}
It is not clear enough what you are trying to accomplish, but under assumption that you want to select all products where ProductID contains any value from specified list, it looks like that it:
public static Product[] GetProducts(string[] prodIDs)
{
return (from p in GetProducts()
where prodIDs.Any(id=>p.ProductID.Contains(id))
select p).ToArray<Product>();
}
Try this
public static Product[] GetProducts(string[] prodIDs)
{
return (
from p in GetProducts()
from q in prodIDs
where p.ProductID.IndexOf(q) > -1
select p)
.ToArray<Product>();
}