Concatenate two columns of an array into a new one - javascript

I have the array range with 3 columns and 10 rows.
How can I concatenate the contents of column 1 with column 2 and push them to a new range dataEnome?
I'm using the following loop, but it isn't very efficient:
var dataEnome =[];
for (i=0; i<range.length; i++){
dataEnome.push(range[i][0])+(range[i][1]);
};
The range looks like this:

For data mapping you can consider using the array.map API.
Example:
var range =
[
[ 'col1.a', 'col2.1', 'c' ],
[ 'col1.b', 'col2.2', '3' ],
[ 'col1.c', 'col2.3', '6' ],
[ 'col1.d', 'col2.4', '9' ],
[ 'col1-e', 'col2.5', '1c' ],
[ 'col1-f', 'col2.6', '6c' ],
[ 'col1-g', 'col2.7', '7c' ],
[ 'col1-h', 'col2.8', '8c' ],
[ 'col1-i', 'col2.9', '9c' ],
[ 'col1-j', 'col2.10', '0c' ],
];
var dataEnome =range.map(row => { return row[0] + row[1]});
console.log(dataEnome);
For more example usages for map;
See:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

If you want to concatenate each record from both column
You may do something like this:
var dataEnome =[];
for (i=0; i<range.getValues().length; i++){
dataEnome.push(range.getValues()[i][0]+range.getValues()[i][1]);
};
Hope this will help you.
Thanks.

... besides dataEnome.push(range[i][0])+(range[i][1]); does most probably feature a broken syntax ...
shouldn't it be dataEnome.push(range[i][0] + range[i][1]); or dataEnome.push(range[i][0].concat(range[i][1]));
... I do not consider the OP's solution to be not that efficient.
One only could transform it into a reusable and more functional style ...
function collectConcatenatedFirstTwoRowColumns(collector, row) {
collector.push(row[0].concat(" / ", row[1]));
return collector;
}
var
range = [
["0, 0", "0, 1", "0, 2"],
["1, 0", "1, 1", "1, 2"],
["2, 0", "2, 1", "2, 2"],
["3, 0", "3, 1", "3, 2"],
["4, 0", "4, 1", "4, 2"],
["5, 0", "5, 1", "5, 2"]
],
dataEnome = range.reduce(collectConcatenatedFirstTwoRowColumns, []);
console.log(dataEnome);

Related

Is there a way to de-dupe a javascript array and combine values of the data?

I'm using an ajax request to grab some XML data which I then need to push into a chart in fusioncharts.
The XML data is formatted as [time taken], [work done], [which team done for], [who did it] (see below).
I'm iterating over the XML and then building the array using the code below:
//Time Recorded
if (columnidchecker == 7781) {
timearray.push($j(this).find('displayData').text());
temp1 = $j(this).find('displayData').text();
}
//Type of Activity
if (columnidchecker == 7782) {
activityarray.push($j(this).find('displayData').text());
temp2 = $j(this).find('displayData').text();
}
//Team Done For
if (columnidchecker == 7783) {
subjectarray.push($j(this).find('displayData').text());
temp3 = $j(this).find('displayData').text();
}
//Name
if (columnidchecker == 7777) {
internalclientarray.push($j(this).find('displayData').text());
temp4 = $j(this).find('userDisplayName').text();
}
});
//PUSH INTO A NEW ARRAY WHICH CAN THEN BE SORTED AND DE-DUPED WITH TIME COMBINED AGAINST ACTIVITY / TEAM.
objectarray.push([temp1, temp2, temp3, temp4]);
This builds an array of entries from the XML which basically outputs to something which looks like this:
0: (4) ["1.50", "Ad-hoc queries or calls", "Team 1", "James"]
1: (4) ["2.50", "Ad-hoc queries or calls", "Team 1", "James"]
2: (4) ["1.00", "Advice", "Team 2", "James"]
3: (4) ["3.50", "Meeting (External 3rd Party)", "Team 1", "James"]
4: (4) ["1.20", "Administration", Team 2", "James"]
5: (4) ["5.50", "Advice", "Team 1", "John"]
I'm trying to build a chart in fusioncharts which needs the format as shown below (ignore foot stuffs - it's taken straight from the fusioncharts help pages!).
{
"chart": {
"theme": "fusion",
"caption": "Revenue split by product category",
"subCaption": "For current year",
"xAxisname": "Quarter",
"yAxisName": "Revenues (In USD)",
"showSum": "1",
"numberPrefix": "$"
},
"categories": [
{
"category": [
{
"label": "Q1"
},
{
"label": "Q2"
},
{
"label": "Q3"
},
{
"label": "Q4"
}
]
}
],
"dataset": [
{
"seriesname": "Food Products",
"data": [
{
"value": "11000"
},
{
"value": "15000"
},
{
"value": "13500"
},
{
"value": "15000"
}
]
},
{
"seriesname": "Non-Food Products",
"data": [
{
"value": "11400"
},
{
"value": "14800"
},
{
"value": "8300"
},
{
"value": "11800"
}
]
}
]
}
The problem i'm having is that I cannot work out how to take the array of data with times, activity, team, name and push them into categories.
I think the first step is to create a new array of names which can be pushed into the "Category" data field in fusioncharts.
I then need a way in which to take the times being recorded against each activity and for each team and make sure it's assigned to the right person within the stacked bar chart and combine the amount of time spent. (i.e. "James" spent a total of 4 hours doing "Ad Hoc Queries and Calls" for Team 1 but this is split across two time entries so I need a way in which to combine them into one.)
Any help on this would be massively appreciated.
I can de-dupe the names to create a new array by using the following code:
namesarray.push(temp4);
uniq = [...new Set(namesarray)];
but after that it starts getting pretty complicated.
Maybe this can help you along the way. It's probably not exactly in the form you want it, but it demonstrates how you could break the problem down into smaller parts.
Pseudo-code:
get the unique names.
get the unique "task" names (for lack of a
better word)
for each unique person name:
3.1. get the data rows for that person
3.2 for each of all unique tasks names:
find the person data rows matching the task name
sum the duration of those data rows
const testData = [
[
"1.50",
"Ad-hoc queries or calls",
"Team 1",
"James"
],
[
"2.50",
"Ad-hoc queries or calls",
"Team 1",
"James"
],
[
"1.00",
"Advice",
"Team 2",
"James"
],
[
"3.50",
"Meeting (External 3rd Party)",
"Team 1",
"James"
],
[
"1.20",
"Administration",
"Team 2",
"James"
],
[
"5.50",
"Advice",
"Team 1",
"John"
]
];
const columnIndexByName = {
TASK_DURATION: 0,
TASK_NAME: 1,
FOR_WHICH_TEAM: 2,
PERSON_DOING_TASK: 3
};
const sum = (acc, next) => acc + next;
const uniqueNames = [...new Set(testData.map(row => row[columnIndexByName.PERSON_DOING_TASK])) ];
const uniqueTaskNames = [...new Set(testData.map(row => row[columnIndexByName.TASK_NAME])) ];
let result = {};
uniqueNames.forEach(personName => {
const personDataRows = testData.filter(row => row[columnIndexByName.PERSON_DOING_TASK] === personName);
let taskDurationsByTaskName = {};
uniqueTaskNames.forEach(taskName => {
const taskRows = personDataRows.filter(row => row[columnIndexByName.TASK_NAME] === taskName);
const taskDurations = taskRows.map(row => Number.parseFloat( row[columnIndexByName.TASK_DURATION] ));
const taskTotalDuration = taskDurations.reduce(sum, 0);
taskDurationsByTaskName[taskName] = taskTotalDuration;
})
result[personName] = taskDurationsByTaskName;
})
const renderData = data => document.querySelector("#output").innerHTML = JSON.stringify(data, null, 2);
renderData(result);
<pre id="output"></pre>

Apply CSS style within .js var

I'm currently building a game which relies on a random number to show a particular string from an array within a .js file. What I want to do is effectively apply a span element to certain text within the array - eg. make positive outcomes green & negative red, however I can't figure out how to do this outside of html. Eg:
var data = [
{"label": "Question 1", "value": 1, "question": "Awareness: High, Consistency: Low."},
{"label": "Question 2", "value": 1, "question": "Consistency: High, Awareness: Low."},
]
A separate set of code, after selecting a random number, returns data[i].label. How would I set a span within js so that the "High" strings return in green, and "Low" return in red?
Cheers.
You have not specified much, but I went and did what I could.
First, your span
<span id="result">here we go</span>
The CSS file, minimalist, I did not include Bootstrap for this.
span {color:green}
.red{color:red}
The JS, the is no event, or event handler.
var data = [
{"label": "Question 1", "value": 1, "question": "Awareness: High, Consistency: Low."},
{"label": "Question 2", "value": 1, "question": "Consistency: High, Awareness: Low."}
]
The array, I have removed the trailing comma after the second object.
let outputs = data[0].question; This will hold the result from the array, data[0] targets the first object, you can also get map over the properties, but you have not entirely specified the scope and desired functionality.
var output = document.getElementById('result'); // the span element
output.innerText = outputs; // i prefer innerText to innerHTML
if (outputs.includes("Consistency: Low")) { // new string method includes, you can pass the whole ("Consistency: Low")in with no worries.
output.classList.toggle("red"); // if condidtion is true, toggle the red class
}
Codepen
https://codepen.io/damPop/pen/ZwvvGV?editors=0010
Uses classes
var data = [
{label: "Question 1", value: 1, awareness: 'High', consistency: 'Low' },
{label: "Question 2", value: 1, awareness: 'Low', consistency: 'High' },
{label: "Question 3", value: 1, awareness: 'Low', consistency: 'Low' },
{label: "Question 4", value: 1, awareness: 'High', consistency: 'High' }
];
const questionElement = document.getElementById('question');
const awarenessElement = document.getElementById('awareness');
const consistencyElement = document.getElementById('consistency');
document.getElementById('select').addEventListener('click', select);
select();
function select() {
const question = data[Math.floor(Math.random() * 4)];
questionElement.innerText = question.label;
awarenessElement.innerText = `Awareness: ${question.awareness}`;
awarenessElement.className = question.awareness;
consistencyElement.innerText = `Consistency: ${question.consistency}`;
consistencyElement.className = question.consistency;
}
.High {
background-color: green;
}
.Low {
background-color: red;
}
<span id="question"></span>
<div id="awareness"></div>
<div id="consistency"></div>
<button id="select">Select</button>

Fetch data from a nested array

I would like to know if there is a way to get the length from a nested array.
My data is a JSON file like this:
{
"data" :[
"item1" :'',
"item2" :[{
"id" :1,
"text":'text'
},{
"id" :2,
"text" : 'text
}]
]
}
I'm using angular 6 and ngx-restangular.
Is possible to get the item 2 length?
The main problem is the question does not provide a valid json. A valid json for the same would be like as under :
{
"data": {
"item1": "",
"item2": [{
"id": 1,
"text": "text"
},
{
"id": 2,
"text": "text"
}
]
}
}
Now you can fetch the second element size simply by
data["item2"].length
or
data.item2.length
To extend the Answer from #AurA
If you had to work with a valid array:
[
[ "item0.0", "item0.1" ],
[ "item1.0", "item1.1" ]
]
you could access the length of the nested arrays like this:
let a = [
["item0.0", "item0.1"],
["item1.0", "item1.1"]
];
let lengthA0 = a[0].length;
let lengthA1 = a[1].length;
console.log("length of a0: ", lengthA0);
console.log("length of a1: ", lengthA1);

Lodash: filter a nested object by multiple properties

Consider the following example:
var products = {
"Products": [{
"Title": "A",
"Categories": [{
"Name": "Type",
"Properties": ["Type 1", "Type 2", "Type 3"]
}, {
"Name": "Market",
"Properties": ["Market 1", "Market 2", "Market 3", "Market 4"]
}, {
"Name": "Technology",
"Properties": ["Tech 1", "Tech 2"]
}]
}, {
"Title": "B",
"Categories": [{
"Name": "Type",
"Properties": ["Type 1", "Type 3"]
}, {
"Name": "Market",
"Properties": "Market 1"
}, {
"Name": "Technology",
"Properties": ["Tech 1", "Tech 3"]
}]
}, {
"Title": "C",
"Categories": [{
"Name": "Type",
"Properties": ["Type 1", "Type 2", "Type 3"]
}, {
"Name": "Market",
"Properties": ["Market 2", "Market 3"]
}, {
"Name": "Technology",
"Properties": ["Tech 2", "Tech 3"]
}]
}]
}
I'm trying to filter products by their properties so consider I'm using an array to keep track of my selected filters:
var filters = ['Type 3', 'Tech 1'];
With these filters I would like to return product A and product B.
I currently have this:
var flattenedArray = _.chain(products).map('Categories').flatten().value();
var result= _.some(flattenedArray , ['Properties', 'Tech 1']);
But I'm stuck on how to combine the properties for a combined search.
Use _.filter() to iterate the products. For each product combine the list of properties using _.flatMap(), and use _.intersection() and _.size() to find the amount of filters that exist in the categories. Compare that to the original number of filters, and return comparison's response.
var products = {"Products":[{"Title":"A","Categories":[{"Name":"Type","Properties":["Type 1","Type 2","Type 3"]},{"Name":"Market","Properties":["Market 1","Market 2","Market 3","Market 4"]},{"Name":"Technology","Properties":["Tech 1","Tech 2"]}]},{"Title":"B","Categories":[{"Name":"Type","Properties":["Type 1","Type 3"]},{"Name":"Market","Properties":"Market 1"},{"Name":"Technology","Properties":["Tech 1","Tech 3"]}]},{"Title":"C","Categories":[{"Name":"Type","Properties":["Type 1","Type 2","Type 3"]},{"Name":"Market","Properties":["Market 2","Market 3"]},{"Name":"Technology","Properties":["Tech 2","Tech 3"]}]}]};
var filters = ['Type 3', 'Tech 1'];
var result = _.filter(products.Products, function(product) {
return filters.length === _(product.Categories)
.flatMap('Properties')
.intersection(filters)
.size();
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
If I understand you question correctly, this code may help:
_.filter(
products.Products,
product => _.difference(
filters,
_.chain(product.Categories).map(category => category.Properties).flatten().value()
).length === 0
);
It calculates a union of all properties for each product:
_.chain(product.Categories).map(category => category.Properties).flatten().value()
And then checks that it contains all filters array elements, using _.difference method.
Hope it helps.
another fancy way through _.conforms
var res = _.filter(
products.Products,
_.conforms({'Categories': function(categories) {
return _.chain(categories)
.flatMap('Properties') // flat arrays
.uniq() // remove dublicates
.keyBy() // transform to objects with Properties keys
.at(filters) // get objects values by filters
.compact() // remove undefineds
.size() // get size
.eq(filters.length) // compare to filters size
.value();
}
}))
This will work for a list of items where the givenProperty you want to filter on is either a string like 'doorColour' or an array of strings representing the path to the givenProperty like ['town', 'street', 'doorColour'] for a value nested on an item as town.street.doorColour.
It also can filter on more than one value so you could you just need pass in an array of substrings representing the string values you want to keep and it will retain items that have a string value which contains any substring in the substrings array.
The final parameter 'includes' ensures you retain these values if you set it to false it will exclude these values and retain the ones that do not have any of the values you specified in the substrings array
import { flatMap, path } from 'lodash/fp';
const filteredListForItemsIncludingSubstringsOnAGivenProperty = (items, givenProperty, substrings, including=true) => flatMap((item) =>
substrings.find((substring) => path(givenProperty)(item) && path(givenProperty)(item).includes(substring))
? including
? [item]
: []
: including
? []
: [item])(items);
E.g. fLFIISOAGP(contacts, ['person','name'], ['Joh','Pau',Pet']);
with items of structure {contact, business:null, personal:{name:'John'}}.
For the original question - this will also work - I would use this repeatedly on a list of items to filter with different keys to filter on more than one property.
const firstFilteredResult = filteredListForItemsIncludingSubstringsOnAGivenProperty(
products.Products,
["Categories", "0", "Properties"],
["Type 3"]);
const secondFilteredResult = filteredListForItemsIncludingSubstringsOnAGivenProperty(
firstFilteredResult,
["Categories", "2", "Properties"],
["Tech 1"]);
expect(secondFilteredResult[0]['Title']).to.equal( "A");
expect(secondFilteredResult[1]['Title']).to.equal( "B");
expect(secondFilteredResult.length).to.equal(2);

Automatically generate names in a javascript array

I have been struggling a lot with a piece of javascript code recently. The code looks like this:
var bigData = {
"teams" : [
["Team 1", "Team 2" ],
["Team 3", "Team 4" ],
["Team 5", "Team 6" ],
["Team 7", "Team 8" ],
["Team 9", "Team 10" ],
["Team 11", "Team 12" ],
["Team 13", "Team 14" ],
["Team 15", "Team 16" ],
],
results : [[ /* WINNER BRACKET */
[[1,0], [1,0], [0,3], [2,3], [1,5], [5,3], [7,2], [1,2]],
[[1,2], [3,4], [5,6], [7,8]],
[[9,1], [8,2]],
[[1,3]]
]
}
As you might have guessed, it's a jquery plugin for tournaments. The problem is that I don't want to write the teams manually, I want them to be written automatically I have done this, and the code doesn't work, because the while loop is inside the variable (so far I know) :
var count = 1;
var bigData = {
"teams" : [
while (count <= 8) {
["Team ".count, "Team ".count ],
count++;
}
],
results : [[ /* WINNER BRACKET */
[[1,0], [1,0], [0,3], [2,3], [1,5], [5,3], [7,2], [1,2]],
[[1,2], [3,4], [5,6], [7,8]],
[[9,1], [8,2]],
[[1,3]]
]
}
It won't work, and I really don't know what to do.
var bigData = {
"teams" : [],
results : [[ /* WINNER BRACKET */
[[1,0], [1,0], [0,3], [2,3], [1,5], [5,3], [7,2], [1,2]],
[[1,2], [3,4], [5,6], [7,8]],
[[9,1], [8,2]],
[[1,3]]
]] };
for( var i=1 ; i<16 ; i+=2 )
bigData.teams.push(['Team '+i,'Team '+(i+1)]);
console.log(JSON.stringify(bigData));
In console:
{"teams":[["Team 1","Team 2"],["Team 3","Team 4"],["Team 5","Team 6"],["Team 7","Team 8"],["Team 9","Team 10"],["Team 11","Team 12"],["Team 13","Team 14"],["Team 15","Team 16"]],"results":[[[[1,0],[1,0],[0,3],[2,3],[1,5],[5,3],[7,2],[1,2]],[[1,2],[3,4],[5,6],[7,8]],[[9,1],[8,2]],[[1,3]]]]}
Try this:
var count = 1;
var data = [];
while (count <= 16) {
data.push(["Team " + (count++).toString(), "Team " + count.toString() ]);
count++;
}
var bigData = {
"teams" : data
}
It builds the array by pushing the teams into it. Note the concatenation of count as a string to the team name, plus the ++ on the first count to iterate it to the next team (thus producing Team 1 and Team 2, not Team 1 and Team 1).
and the code doesnt work, because the while loop is inside the variable
Yes, you cannot put control flow statements in the midst of an object literal. Instead, you will need to start with an empty array, and then fill that with values via assignments (or push):
var teams = [];
for (var i=1; i<=16; )
teams.push(['Team '+i++,'Team '+i++]);
var bigData = {
teams: teams,
results: …
};
The other answers provided here build a list of teams using a set number (8, 16, whatever).
You can get a little cheeky and generate the list of teams off the teams in the actual results instead, which will future-proof your script against your league size expanding. Code spends most of its lifetime in maintenance, if you can spend just a few extra minutes to make your stuff more data-driven, you'll save plenty of time over the long haul.
Since you're using jquery, I'll make some use of the functions in that library here:
var bigData = {
results : [
[ /* WINNER BRACKET */
[[1,0], [1,0], [0,3], [2,3], [1,5], [5,3], [7,2], [1,2]],
[[1,2], [3,4], [5,6], [7,8]],
[[9,1], [8,2]],
[[1,3]]
]
]
};
function flatten(n) {
return $.map(n, function(x){
return $.isArray(x) ? flatten(x) : x;
});
}
bigData.teams = $.map(
$.unique(flatten(bigData.results)).sort(),
function(num) { return "Team "+num; }
);
Console output:
> JSON.stringify(bigData)
"{
"results":[[[[1,0],[1,0],[0,3],[2,3],[1,5],[5,3],[7,2],[1,2]],[[1,2],[3,4],[5,6],[7,8]],[[9,1],[8,2]],[[1,3]]]],
"teams":["Team 0","Team 1","Team 2","Team 2","Team 2","Team 3","Team 4","Team 5","Team 6","Team 7","Team 8","Team 8","Team 9"]
}"

Categories

Resources