Related
I'm currently working on trying to create a UDF to split a key value pair string based on web traffic into JSON.
I've managed to get as far as outputting a JSON object but I'd like to be able to dynamically add nested items based on the number of products purchased or viewed based on the index number of the key.
When a product is only viewed, there is always only one product in the string. Only when its a transaction is it more than one but I think it would be good to conform the structure of the json and then identify a purchase or view based on the presence of a transactionid. For example:
Item Purchased:
sessionid=12345&transactionid=555555&product1=apples&productprice1=12&product1qty=1&product2=pears&productprice2=23&product2qty=3&transactionamount=58
The output should look something like this:
[
{
"sessionid":12345,
"transactionid":555555,
"transactionamount":58
},
[
{
"productline":1,
"product":"apples",
"productprice":12,
"productqty":1
},
{
"productline":2,
"product":"pears",
"productprice":23,
"productqty":2
}
]
]
Item Viewed:
sessionid=12345&product1=apples&productprice1=12&product1qty=1&product2=pears&productprice2=23&product2qty=3
[
{
"sessionid":12345,
"transactionid":0,
"transactionamount":0
},
[
{
"productline":1,
"product":"apples",
"productprice":12,
"productqty":1
}
]
]
The result I'll be able to parse from JSON into a conformed table in a SQL table.
What I've tried so far is only parsing the string, but its not ideal to create a table in SQL because the number of purchases can vary:
var string = "sessionid=12345&transactionid=555555&product1=apples&productprice1=12&product1qty=1&product2=pears&productprice2=23&product2qty=3&transactionamount=58";
function splitstring(queryString) {
var dictionary = {};
if (queryString.indexOf('?') === 0) {
queryString = queryString.substr(1);
}
var parts = queryString.split('&');
for (var i = 0; i < parts.length; i++) {
var p = parts[i];
// Step 2: Split Key/Value pair
var keyValuePair = p.split('=');
var key = keyValuePair[0];
var value = keyValuePair[1];
dec_val = decodeURIComponent(value);
final_value = dec_val.replace(/\+/g, ' ');
dictionary[key] = final_value;
}
return (dictionary);
}
console.log(splitstring(string));
Thanks in advance!!!
Feel like this would be less clunky with better param naming conventions, but here's my take...
function parseString(string) {
var string = string || '',
params, param, output, i, l, n, v, k, pk;
params = string.split('&');
output = [{},
[]
];
for (i = 0, l = params.length; i < l; i++) {
param = params[i].split('=');
n = param[0].match(/^product.*?([0-9]+).*/);
v = decodeURIComponent(param[1] || '');
if (n && n[1]) {
k = n[1];
output[1][k] = output[1][k] || {};
output[1][k]['productline'] = k;
pk = n[0].replace(/[0-9]+/, '');
output[1][k][pk] = v;
} else {
output[0][param[0]] = v;
}
}
output[1] = output[1].filter(Boolean);
return output;
}
var string = "sessionid=12345&transactionid=555555&product1=apples&productprice1=12&product1qty=1&product2=pears&productprice2=23&product2qty=3&transactionamount=58";
console.log(parseString(string));
output:
[
{
"sessionid": "12345",
"transactionid": "555555",
"transactionamount": "58"
},
[{
"productline": "1",
"product": "1",
"productprice": "12"
}, {
"productline": "2",
"product": "3",
"productprice": "23"
}]
]
There's probably a far nicer way to do this, but I just wrote code as I thought about it
var string = "sessionid=12345&transactionid=555555&product1=apples&productprice1=12&product1qty=1&product2=pears&productprice2=23&product2qty=3&transactionamount=58";
function splitstring(queryString) {
var dictionary = {};
if (queryString.indexOf('?') === 0) {
queryString = queryString.substr(1);
}
var parts = queryString.split('&');
for (var i = 0; i < parts.length; i++) {
var p = parts[i];
// Step 2: Split Key/Value pair
var keyValuePair = p.split('=');
var key = keyValuePair[0];
var value = keyValuePair[1];
dec_val = decodeURIComponent(value);
final_value = dec_val.replace(/\+/g, ' ');
dictionary[key] = final_value;
}
return (dictionary);
}
function process(obj) {
let i = 1;
const products = [];
while(obj.hasOwnProperty(`product${i}`)) {
products.push({
[`product`]: obj[`product${i}`],
[`productprice`]: obj[`productprice${i}`],
[`productqty`]: obj[`product${i}qty`]
});
delete obj[`product${i}`];
delete obj[`productprice${i}`];
delete obj[`product${i}qty`];
++i;
}
return [obj, products];
}
console.log(process(splitstring(string)));
By the way, if this is in the browser, then splitstring can be "replaced" by
const splitstring = string => Object.fromEntries(new URLSearchParams(string).entries());
var string = "sessionid=12345&transactionid=555555&product1=apples&productprice1=12&product1qty=1&product2=pears&productprice2=23&product2qty=3&transactionamount=58";
function process(string) {
const splitstring = queryString => {
var dictionary = {};
if (queryString.indexOf('?') === 0) {
queryString = queryString.substr(1);
}
var parts = queryString.split('&');
for (var i = 0; i < parts.length; i++) {
var p = parts[i];
// Step 2: Split Key/Value pair
var keyValuePair = p.split('=');
var key = keyValuePair[0];
var value = keyValuePair[1];
dec_val = decodeURIComponent(value);
final_value = dec_val.replace(/\+/g, ' ');
dictionary[key] = final_value;
}
return (dictionary);
};
let i = 1;
const obj = splitstring(string);
const products = [];
while (obj.hasOwnProperty(`product${i}`)) {
products.push({
[`product`]: obj[`product${i}`],
[`productprice`]: obj[`productprice${i}`],
[`productqty`]: obj[`product${i}qty`]
});
delete obj[`product${i}`];
delete obj[`productprice${i}`];
delete obj[`product${i}qty`];
++i;
}
return [obj, products];
}
console.log(process(string));
I have an array in JavaScript that have defined these values:
var myStringArray = ["1","2","3","4","5","6","7","8","9","10"];
And when I call a function the first time function, I need to get this:
1
2
3
Calling it again I need to get:
4
5
6
Calling it again:
7
8
9
Calling it again:
10
1
2
Calling again:
3
4
5
And so on. You got the point, showing 3 values from the array and if we are at the end of array, read from the beginning... I have an app that has remote control and has down and up keys. When the user presses the down button to get these values from an array as described in the above example, if the user presses the up button it needs to go back from an example...so reading the array in a loop (at end, the array is read from the beginning, but always shows three values).
I try using this:
var myStringArray = ["1","2","3","4","5","6","7","8","9","10"];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
if (i<(6)) {
console.log(myStringArray[i]);
}
}
But the next time I call this code, it shows from the beginning values of the array, not continue to read others value...do I need a second counter?
If you are OK with manipulating your original array as you loop through it you could splice and concat similar to below (or you could use a clone of the array if you need to persist the original array):
var myStringArray = ["1","2","3","4","5","6","7","8","9","10"];
var loopByX = function(x){
var y = myStringArray.splice(0,x);
myStringArray = myStringArray.concat(y);
return y;
}
console.log(loopByX(3));
console.log(loopByX(3));
console.log(loopByX(3));
console.log(loopByX(3));
console.log(loopByX(3));
If you want to go bi-directional (is that what you call it?), as mentioned in the comments, you could do it as below which then gives you the ability to go backwards or forward and the flexibility to do so in an arbitrary number:
var myStringArray = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
var loopByX = function(x) {
var len = myStringArray.length;
// Ensure x is always valid but you can add any behaviour here in that case yourself. As an example I return an empty array.
if (Math.abs(x) > len) {
return [];
}
var y = x > 0 ? myStringArray.splice(0, x) : myStringArray.splice(len + x, len);
myStringArray = x > 0 ? myStringArray.concat(y) : y.concat(myStringArray);
return y;
}
console.log(loopByX(20)); // invalid number
console.log(loopByX(-20)); // invalid number
console.log(loopByX(-3));
console.log(loopByX(-6));
console.log(loopByX(3));
console.log(loopByX(4));
You could take a function which slices three elements and if not possible, it takes the needed first values of the array as well.
function take3() {
var temp = array.slice(index, index += 3)
index %= array.length;
console.log(temp.concat(temp.length < 3 ? array.slice(0, index) : []).join(' '));
}
var array = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
index = 0;
<button onclick="take3()">take 3</button>
With a mapping of a dynamic count.
function take(length) {
console.log(Array.from({ length }, _ => array[++index, index %= array.length]));
}
var array = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
index = -1;
<button onclick="take(3)">take 3</button>
Your variable i is local to the for loop which means it basically resets every time the loop is started. So first make your variable i global.
var i=0;
function employeeNames(){
var empList = ["1","2","3","4","5","6","7","8","9","10"];
var output = [];
var j=0;
while(j<3)
{
output.push(empList[i])
i=(i+1)%empList.length;
j++;
}
return output;
}
console.log(employeeNames());
console.log(employeeNames());
console.log(employeeNames());
console.log(employeeNames());
console.log(employeeNames());
If you want the immutable way to achieve your circular looping
function loopArray(arr, step=3) {
let i = 0;
return function inner() {
for (let j = 0; j < step; j++) {
console.log(arr[i]);
i = (i + 1) % arr.length;
}
};
}
const func = loopArray(["1","2","3","4","5","6","7","8","9","10"], 3);
func();
func();
func();
func();
func();
The fancy solution with generator functions:
function* cycle(arr) {
let i=0;
while (true) {
yield arr[i++];
i %= arr.length;
}
}
function* chunksOf(n, iterable) {
let chunk = [];
for (const x of iterable) {
chunk.push(x)
if (chunk.length >= n) {
yield chunk;
chunk = [];
}
}
if (chunk.length > 0)
yield chunk;
}
function toFunction(iterator) {
return () => iterator.next().value;
}
var myStringArray = ["1","2","3","4","5","6","7","8","9","10"];
const f = toFunction(chunksOf(3, cycle(myStringArray)));
console.log(f());
console.log(f());
console.log(f());
// …
#Igor Petev, JavaScript's closures are a nice concept that you can use to solve your problem.
Please read JavaScript's Closures - w3schools article. It's really nice and excellent.
I have used the concept of closures to solve this problem. Please leave a comment if you don't understand my code or anything else related to this problem.
Please have a look at the below code.
var get3items = (function () {
var index = 0;
var myStringArray = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
var len = myStringArray.length
return function () {
for(var count = 0; count < 3; count += 1)
{
console.log(myStringArray[index]);
if(index == (len - 1))
{
index = 0;
}
else {
index += 1;
}
}
}
})();
get3items (); // First call
console.log()
get3items (); // Second call
console.log()
get3items (); // Third call
console.log()
get3items (); // Fourth call
console.log()
get3items (); // Fifth call
/*
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
*/
How about using a generator:
function* get3() {
var myStringArray = ["1","2","3","4","5","6","7","8","9","10"];
var index = 0;
while (true) {
yield [0, 1, 2].map(i => myStringArray[(index + i) % myStringArray.length])
index = (index + 3) % myStringArray.length;
}
}
Calling this function returns an object which you can call .next() on, to get the next set of 3:
var getter = get3();
console.log(getter.next().value); // ["1","2","3"]
console.log(getter.next().value); // ["4","5","6"]
console.log(getter.next().value); // ["7","8","9"]
// etc.
function* employeeNames(){
var empList = ["1","2","3","4","5","6","7","8","9","10"];
for(var i =0; i<=empList.length; i++){
yield empList[i];
}
}
var emp;
emp = employeeNames();
It uses a generator function...
I have got a fruits array as shown below
var fruits = [
{
"buyprice": "10",
"sellprice": "11",
"name": "pomogranate"
},
{
"buyprice": "10",
"sellprice": "11",
"name": "apple"
},
{
"buyprice": "12",
"sellprice": "13",
"name": "orange"
},
{
"buyprice": "14",
"sellprice": "15",
"name": "apple"
}
]
I want to find out the total counts of apple present in the array
and the buy price and sellprice of the first element of the apple present in the array (not the last element)
I have tried it this way
function findnumberofaccourences(fruits, apple) {
var a = 0;
var buyprice;
var sellprice
for (var i = 0; i < fruits.length; i++) {
var name = fruits[i].name;
buyprice = fruits[i].buyprice;
sellprice = fruits[i].sellprice;
if (name == apple) {
a++;
}
}
var p = { count: a, buyprice: buyprice, sellprice: sellprice };
return p;
}
var result = findnumberofaccourences(fruits, 'apple');
alert(JSON.stringify(result));
But when i run this i am getting the result as (the buyprice and sellprice of apple's last element , where as i need apples first element )
{"count":2,"buyprice":"14","sellprice":"15"}
This is my fiddle
http://jsfiddle.net/thkc0fpk/2/
could you please elt em know how to achive this
A few things:
As Hacketo said, only grab the prices when a == 0.
Also, only grab the price for apples, rather than all fruit.
Calling the argument giving the fruit name to find apple is a bit misleading (since it could contain the string pear). Perhaps fruitName or similar instead?
Here's a minimal-changes example:
function findnumberofaccourences(fruits, fruitName) {
var a = 0;
var buyprice;
var sellprice;
for (var i = 0; i < fruits.length; i++) {
var name = fruits[i].name;
if (name == fruitName) { // Only apples
if (a == 0) { // Only the first
buyprice = fruits[i].buyprice;
sellprice = fruits[i].sellprice;
}
a++;
}
}
var p = {
count: a,
buyprice: buyprice,
sellprice: sellprice
};
return p;
}
var result = findnumberofaccourences(fruits, 'apple');
alert(JSON.stringify(result));
You can also simplify a fair bit, using the more-modern forEach and using the object you're going to return directly:
function findnumberofaccourences(fruits, fruitName) {
var p = {
count: 0,
buyprice: undefined,
sellprice: undefined
};
fruits.forEach(function(fruit) {
if (fruit.name == fruitName) {
if (p.count == 0) {
p.buyprice = fruit.buyprice;
p.sellprice = fruit.sellprice;
}
p.count++;
}
});
return p;
}
var result = findnumberofaccourences(fruits, 'apple');
alert(JSON.stringify(result));
You can filter apples first into an array, then retrieve the first element of this array to get the data you need.
var apples = fruits.filter(function (fruit) {
return fruit.name === 'apple';
});
var p = {
count: apples.length,
buyprice: apples[0].buyprice,
sellprice: apples[0].sellprice
};
Just reverse the array first
var price = {
count: 0
};
fruits.reverse().forEach(function (fruit) {
if (fruit.name === "apple") {
price.count++;
price.buyprice = fruit.buyprice;
price.sellprice = fruit.sellprice;
}
})
console.log(price)
// restore original order in fruits array (if you are going to use it)
fruits.reverse();
Note that this will reverse your array in place, so you'll need to do a fruits.reverse() once again to get back the fruits array elements in the original order, if you are going to use fruits further down the flow. (Thanks aduch)
Well your code does not work as you expect, your example work only because apple is the last element of the array.
you need to update your for loop like this
for (var i = 0; i < fruits.length; i++) {
var name = fruits[i].name;
if (name == apple) {
if ( a === 0){
buyprice = fruits[i].buyprice;
sellprice = fruits[i].sellprice;
}
a++;
}
}
It will update buyprice and sellprice only the first time for the first apple
Your orignal code was updating buyprice and sellprice for each element of your array
I have a table that follows a defined sequence that Repite : [ col , name , value1, value2 , value 3, col, name value1, value2, value3 ..col , name , value1, value2 , value 3 ]
code:
var data =["DN","Atac","1","2","3","PDA","Atac","5","6","7","EPDA","Atac","8","9","11","DN Potentielle","Atac","14","4","8"] ;
I try to split the data table col , name , values:
Code result :
var column = ["DN","PDA","EPDA","DN Potentielle"];
var name ="Atac";
var values =[ "1","2","3","5","6","7","8","9","11","14","4","8"];
how has the simplest method without a lot of code to do it
If you're sure that your data is consistent and can rely on the structure you wrote, the simplest thing would be:
var column = [];
var name = [];
var values = [];
for (var i = 0; i < data.length; i = i+5) {
column.push(data[i]);
name.push(data[i+1]);
values.push(data[i+2]);
values.push(data[i+3]);
values.push(data[i+4]);
};
name = name.filter(function(value, index, self) {
return self.indexOf(value) === index;
});
console.log(column); //["DN","PDA","EPDA","DN Potentielle"]
console.log(name); //["Atac"]
console.log(values); //["1", "2", "3", "5", "6", "7", "8", "9", "11", "14", "4", "8"]
Fiddle
Set up an object to hold the values:
var obj = { column: [], name: null, values: [] };
Then cycle over the array in groups of 5, adding the various elements to the object:
for (var i = 0, l = data.length; i < l; i+=5) {
obj.column.push(data[i]);
obj.name = data[i + 1];
// push.apply is a neat way of concatenation that doesn't
// require the creation of a new variable
obj.values.push.apply(obj.values, data.slice(i + 2, i + 5));
}
DEMO
I try to figure out what you want, and the best way is to retrieve the number value and string value without spliting it.
var data =["DN","Atac","1","2","3","PDA","Atac","5","6","7","EPDA","Atac","8","9","11","DN Potentielle","Atac","14","4","8"] ;
var columns = [];
var values = [];
$.each(data, function(k, v) {
var parsedValue = parseInt(data[k]);
if ( ! isNaN(parsedValue) ) {
values.push(parsedValue);
} else {
columns.push(v);
}
});
console.log(columns);
console.log(values);
Demo: http://jsfiddle.net/bzryqs84/1/
window.onload = function () {
x = '';
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, {a:x, b:''} ];
for (i = 0; i < myArray.length; i += 1) {
x = myArray[i].a + myArray[i].b;
}
alert(x); // alerts '';
}
Hi, the above is an example of what I'm trying to do. Basically, I would like for x to be evaluated after the 2nd array element computes it. I think this is called lazy evaluation, but not sure... I'm somewhat new.
How can I process my array in the loop and x be evaluated each time such that when I get to the third iteration, x = 'cd' and will alert as 'cd'?
I think I figured out the answer with your help and the other thread I mentioned in the comment. Just need to wrap x in a function and then define a get function to apply to all elements:
window.onload = function () {
function get(e) {return (typeof e === 'function') ? e () : e; }
var x = '';
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, {a:function() {return x; }, b:''} ];
for (i = 0; i < myArray.length; i += 1) {
x = get(myArray[i].a) + get(myArray[i].b);
}
alert(x); // alerts 'cd';
}
x can be anything then. For example (x + 'xyz') will alert 'cdxyz'. So this way I can have any variable that I want evaluated later (when needed) be evaluated correctly (based on state at that point).
That's what I needed. :)
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function getter(list, num) {
var i, agg = { a: "", b: "" };
for (i = 0; i <= num; i += 1) {
agg.a += list[i].a;
}
return agg;
}
console.log(getter(elements, 0).a); // "a"
console.log(getter(elements, 1).a); // "ac"
console.log(getter(elements, 2).a); // "ace"
You can use a closure so you can't access the values, like:
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function make_getter(list) {
return {
get: function (num) {
var i, agg = { a: "", b: "" };
for (i = 0; i <= num; i += 1) {
agg.a += list[i].a;
}
return agg;
}
};
}
var getter = make_getter(elements);
console.log(getter.get(0).a); // "a"
console.log(getter.get(1).a); // "ac"
console.log(getter.get(2).a); // "ace"
You can make different implementations of the aggregation function.
With recursion:
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function getter(list, num) {
var i, agg = list[num];
if (num > 0) {
agg.a = getter(list, num-1).a + agg.a;
}
return agg;
}
console.log(getter(elements, 0).a); // "a"
console.log(getter(elements, 1).a); // "ac"
console.log(getter(elements, 2).a); // "aace" <-- note, elements are actually modified!
console.log(getter(elements, 2).a); // "aaacaace" <-- note, elements are actually modified!
old answer
Since x is not an object it's value will be copied, rather than passed as a reference.
If you change your code to:
var element = { a: '', b:'' };
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, element ];
for (i = 0; i < myArray.length; i += 1) {
element.a = myArray[i].a + myArray[i].b;
}
alert(el.a); // alerts 'cd';
You will get "cd".
This is not called lazy evaluation by the way. It's just an aggregate or something.