I have an array of object
const parameters = [
{token: '78fe6df3f'},
{id: '12345'},
{ price: '0 - 9,000,000' },
{ 'area[]': 'Applehead Island' },
{ 'waterfront_type[]': 'Open Water' },
{ property_type_single: 'Single Family/Site Built' },
{ bedrooms: '0 - 5' },
{ baths: '0 - 5' },
{ sqft: '0 - 7500' }
];
I want this object to be turned to like below
https://www.example.com/properties.php?token=78fe6df3f&id=12345&price=$0%20-%20$3,480,000&area[]=Applehead%20Island&waterfront_type[]=Open%20Water&property_type_single=Single%20Family/Site%20Built&bedrooms=0%20-%205&baths=0%20-%205&sqft=0%20-%207500
Please help on how to get this?
Assuming that this is JavaScript, you can use the encodeURIComponent() function to URL-encode all of your key-value pairs as you have shown. Simply iterate over the array and concatenate the URL-encoded values:
const parameters = [
{ token: '78fe6df3f'},
{ id: '12345'},
{ price: '0 - 9,000,000' },
{ 'area[]': 'Applehead Island' },
{ 'waterfront_type[]': 'Open Water' },
{ property_type_single: 'Single Family/Site Built' },
{ bedrooms: '0 - 5' },
{ baths: '0 - 5' },
{ sqft: '0 - 7500' }
];
let uri = "https://example.org/properties.php?";
let firstSegment = true;
for(const param of parameters) {
if(!firstSegment) {
uri += "&";
firstSegment = false;
}
// find out the name of this object's property
const paramName = Object.keys(param)[0];
uri += paramName + "=" + encodeURIComponent(param[paramName]);
}
console.log(uri);
This can be written more concisely using map() and join():
const parameters = [
{ token: '78fe6df3f'},
{ id: '12345'},
{ price: '0 - 9,000,000' },
{ 'area[]': 'Applehead Island' },
{ 'waterfront_type[]': 'Open Water' },
{ property_type_single: 'Single Family/Site Built' },
{ bedrooms: '0 - 5' },
{ baths: '0 - 5' },
{ sqft: '0 - 7500' }
];
let uri = "https://example.org/properties.php?" +
parameters
.map(
param => {
const name = Object.keys(param)[0];
return name + "=" + encodeURIComponent(param[name]);
})
.join("&");
console.log(uri);
You could merge all objects into one, go over its properties and merge it to one string:
const result = encodeURIComponent(Object.entries(Object.assign({}, ...parameters)).map(([key, value]) => key + "=" + value).join("&"));
Step by step:
[{ k: v }, { k2, v2 }]
1) Object.assign({}, ...parameters)
{ k: v, k2: v2 }
2) Object.entries(...)
[[k, v], [k2, v2]]
3) .map(([key, value]) => key + "=" + value)
["k=v", "k2=v2"]
4) .join("&")
"k=v&k2=v2"
My goal in stack overflow is to explain concepts in a very simplified manner .I have commented in the code , so you will understand every step .
The ${} are ES6 concepts , if you have never seen them , refer to this article
Thanks for challenging me.
So here is my solved code , short and easy .
var parameters = [
{token: '78fe6df3f'},
{id: '12345'},
{ price: '0 - 9,000,000' },
{ 'area[]': 'Applehead Island' },
{ 'waterfront_type[]': 'Open Water' },
{ property_type_single: 'Single Family/Site Built' },
{ bedrooms: '0 - 5' },
{ baths: '0 - 5' },
{ sqft: '0 - 7500' }
];
//we initialize an empty variable
var serializedString = '';
//.map() This loop will loop through the array to pick each object
parameters.map((i)=>{
//for-in This loop goes through the key-value inside of each object
for(var key in i){
//here we are assigning the stringed values into the variable we declared earlier on
serializedString +=`${key}=${i[key]}&`;
}
});
//after all this is done , we convert our string into a URL friendly string
var ourURL_friendlyResult = encodeURIComponent(serializedString);
//we console it out
console.log(`https://example.org/properties.php?${ourURL_friendlyResult}`);
Related
I am having two array with the same length and format given at the end.
assume the last element on each array is the score if either array has zero values in other elements.
Let's say we have array p1 and p2 each have 7 elements. If either p1 or p2 first 6 elements has zero value then it means the game is over and we sum up all other elements and add to last element(mail_hole) which define its score. Then compare each score to find the winner.
Here is my code:
function checkWinner(holes, status = "incomplete", winner = "none") {
const p1MainHole = holes["p1"].pop(); // check if all holes has zero stone.(Except main hole)
const p2MainHole = holes["p2"].pop(); // check if all holes has zero stone.(Except main hole)
if (holes["p1"].every((hole) => hole.value === 0)) {
const sumOfAllStone = this.countAllStone(holes, "p2", p2MainHole);
holes["p2"].push(sumOfAllStone);
holes["p1"].push(p1MainHole);
status = "complete";
} else if (holes["p2"].every((hole) => hole.value === 0)) {
const sumOfAllStone = this.countAllStone(holes, "p1", p1MainHole);
holes["p1"].push(sumOfAllStone);
holes["p2"].push(p2MainHole);
status = "complete";
} else {
holes["p1"].push(p1MainHole);
holes["p2"].push(p2MainHole);
}
if (status === "complete") {
winner = holes["p1"][holes["p1"].length - 1].value > holes["p2"][holes["p2"].length - 1].value ? "p1" : "p2";
}
return {
holes,
status,
winner
};
}
function countAllStone(holes, player, mainHole) {
for (let i = 0; i < holes[player].length; i++) {
mainHole.value += holes[player][i].value;
}
return mainHole;
}
console.log(
checkWinner({
p1: [
{
name: "hole_0",
value: 0,
},
{
name: "hole_1",
value: 0,
},
{
name: "hole_2",
value: 0,
},
{
name: "hole_3",
value: 0,
},
{
name: "hole_4",
value: 0,
},
{
name: "hole_5",
value: 0,
},
{
name: "main_hole",
value: 0,
},
],
p2: [
{
name: "hole_0",
value: 1,
},
{
name: "hole_1",
value: 1,
},
{
name: "hole_2",
value: 1,
},
{
name: "hole_3",
value: 1,
},
{
name: "hole_4",
value: 2,
},
{
name: "hole_5",
value: 0,
},
{
name: "main_hole",
value: 1,
},
],
})
);
At the end it compares each player's score(last elements) to find the winner.
I am not satisfied with the amount of code written and the efficiency of it. Any idea would be welcome, Thanks.
This may be one possible alternate solution to achieve the desired objective:
Code Sample
if (allZeroValues(p1) || allZeroValues(p2)) {
resObj.status = 'complete';
if (allZeroValues(p1)) updateTotal(p2);
else updateTotal(p1);
resObj.winner = getWinner(p1, p2);
};
Explanation
if either p1 or p2 are zero-valued (except 'main_hole'), then
set status to complete
if p1 is all zeroes, update p2's total
else, update p1's total
set winner based on the totals
There are several helper methods used which may be understood from perusing the snippet below.
Code Snippet
const checkWinner = (holes, status = "incomplete", winner = "none") => {
// first, declare few helper methods
// to get an array without the 'main_hole'
const skipMainHole = arr => ([
...arr.filter(el => el.name !== 'main_hole')
]);
// add total values except 'main_hole'
const sumValues = arr => (
skipMainHole(arr).reduce(
(tot, itm) => (tot + itm.value),
0
)
);
// check if array without 'main_hole' is all zeroes
// assumption: 'value' will always be non-negative integer
const allZeroValues = arr => (sumValues(arr) === 0);
// update 'main_hole' value
const updateTotal = arr => {
arr[arr.length - 1].value += sumValues(arr);
};
// get winner
const getWinner = (arr1, arr2) => (
arr1.slice(-1)[0].value === arr2.slice(-1)[0].value
? 'none'
: arr1.slice(-1)[0].value > arr2.slice(-1)[0].value
? 'p1'
: 'p2'
);
// now, de-structure holes to get the p1, p2 arrays
const {p1, p2} = holes;
// set-up a result-object
const resObj = {status, winner};
// now, for the actual logic
if (allZeroValues(p1) || allZeroValues(p2)) {
resObj.status = 'complete';
if (allZeroValues(p1)) updateTotal(p2);
else updateTotal(p1);
resObj.winner = getWinner(p1, p2);
};
// finally, return the updated result-object
return {...resObj, holes: {p1, p2}};
};
console.log(
checkWinner({
p1: [
{
name: "hole_0",
value: 0,
},
{
name: "hole_1",
value: 0,
},
{
name: "hole_2",
value: 0,
},
{
name: "hole_3",
value: 0,
},
{
name: "hole_4",
value: 0,
},
{
name: "hole_5",
value: 0,
},
{
name: "main_hole",
value: 0,
},
],
p2: [
{
name: "hole_0",
value: 1,
},
{
name: "hole_1",
value: 1,
},
{
name: "hole_2",
value: 1,
},
{
name: "hole_3",
value: 1,
},
{
name: "hole_4",
value: 2,
},
{
name: "hole_5",
value: 0,
},
{
name: "main_hole",
value: 1,
},
],
})
);
Im trying to build a new array of objects based on specs array of objects.
There are some similar question already but didn't help me.
const specs =
[
{ label: 'Brand', value: 'Nike' },
{ label: 'Age range', value: '8 to 13' },
{ label: 'Age range', value: '14+' }
]
I want to skip the "Brand", which works with my code, and I want that if there are multiple objects with the same label, they need
to be merged in a unique object which contains that label and ALL of the values together, so the output will have brand removed and the other values merged:
[
{ label: 'Age range', value: '8 to 13 - 14+' }
]
Here's my code:
var newSpecs = []
for (let x = 0; x < specs.length; x++) {
if (specs[x].label === 'Brand') continue
for (let j = x + 1; j < specs.length; j++) {
if (specs[x].label === specs[j].label) {
specs[x].value += ' - ' + specs[j].value
}
}
newSpecs.push({ label: specs[x].label, value: specs[x].value })
}
return newSpecs
But this simply does not work, it created multiple merged age range.
I tried another time by using delete and other things but got me "cannot read label of undefine", so I'll keep this way,
but I cannot figure out how to do it
You receive multiple "Age range" because you have nested loops. Try this approach using .reduce() for grouping:
const specs = [
{ label: 'Brand', value: 'Nike' },
{ label: 'Age range', value: '8 to 13' },
{ label: 'Age range', value: '14+' }
];
var newSpecs = specs.reduce((res, curr) => {
if (curr.label === 'Brand') return res;
var group = res.find((el) => el.label === curr.label);
if (group) {
group.value += ' - ' + curr.value;
} else {
res.push(curr);
}
return res;
}, []);
console.log(newSpecs);
You can use map to separate out labels
const specs = [
{ label: "Brand", value: "Nike" },
{ label: "Age range", value: "8 to 13" },
{ label: "Age range", value: "14+" },
];
const labelMap = {};
specs.forEach((sp) => {
if (sp.label !== "Brand") {
labelMap[sp.label] = labelMap[sp.label] || { label: sp.label, value: [] };
labelMap[sp.label].value.push(sp.value);
}
});
const op = Object.values(labelMap).map((el) => ({
...el,
value: el.value.join(" - "),
}));
console.log(op);
I have an array with three levels of nesting and a one dimensional object.I need to compare the two to find matching ID's and push them into a new array as pairs. I'm just using the map method here, but perhaps there's a more efficient way to do this? I've thought of using the filter method, but I don't think it can work in this case.
THE FUNCTION:
const getMatchingIDs = function (pages, storedOBJ) {
const arr = []
pages.map((page)=> {
return page.questions.map((q)=>{
const questionText = q.questionText
return Object.keys(storedOBJ).map((key) => {
const answerIndex = storedOBJ[key]
if (typeof answerIndex !== 'undefined' && questionText === key) {
const answerID = q.answers[answerIndex].id
arr.push( q.id + ':' + answerID)
}
})
})
})
return arr
}
THE ARRAY AND OBJECT:
const pages = [
{
questions: [
{
id: 987,
questionText: 'Some Question',
answers: [
{
id: 154
},
{
id: 232
},
{
id: 312
}
]
},
{
id: 324,
questionText: 'Another Question',
answers: [
{
id: 154
},
{
id: 232
},
{
id: 312
}
]
},
{
id: 467,
questionText: 'Last Question',
answers: [
{
id: 154
},
{
id: 232
},
{
id: 312
}
]
}
]
}
]
const storedOBJ = {
'Some Question': 0,
'Last Question': 0,
'Another Question': 2
}
Running getMatchingIDs(pages, storedOBJ) should return ["987:154", "324:312", "467:154"]
Your use of 'map'
So for one thing, you are using the 'map' method where it would be better to use other array methods such as 'forEach' or 'reduce'. The the function passed in to the 'map' method is supposed to return an element for the new array. You are using the 'map' method just to iterate the arrays without capturing the result.
Example #1
Here is a slightly modified version of your 'matchIDs' function. The first reduce flattens the pages to make a single list of questions. The second reduce produces your matches, and skips conditions where the answer index is undefined.
function matchIDs(pages, answerMap) {
const questions = pages.reduce((questions, page) => { return questions.concat(page.questions) }, []);
return questions.reduce((matches, question) => {
const answerIndex = answerMap[question.questionText];
if(typeof answerIndex != 'undefined') matches.push(`${question.id}:${question.answers[answerIndex].id}`);
return matches;
}, []);
}
Example #2
In your example data you have only one page and all of your answer indexes are valid. If you can make these assumptions you could simplify further:
function matchIDs(questions, answerMap) {
return questions.map(question => {
const answerIndex = answerMap[question.questionText];
return `${question.id}:${question.answers[answerIndex].id}`;
});
}
Runnable snippet
const pages = [
{
questions: [
{
id: 987,
questionText: 'Some Question',
answers: [
{
id: 154
},
{
id: 232
},
{
id: 312
}
]
},
{
id: 324,
questionText: 'Another Question',
answers: [
{
id: 154
},
{
id: 232
},
{
id: 312
}
]
},
{
id: 467,
questionText: 'Last Question',
answers: [
{
id: 154
},
{
id: 232
},
{
id: 312
}
]
}
]
}
];
const storedOBJ = {
'Some Question': 0,
'Last Question': 0,
'Another Question': 2
};
function matchIDs(pages, answerMap) {
const questions = pages.reduce((questions, page) => { return questions.concat(page.questions) }, []);
return questions.reduce((matches, question) => {
const answerIndex = answerMap[question.questionText];
if(typeof answerIndex != 'undefined') matches.push(`${question.id}:${question.answers[answerIndex].id}`);
return matches;
}, []);
}
function matchIDsAlt(questions, answerMap) {
return questions.map(question => {
const answerIndex = answerMap[question.questionText];
return `${question.id}:${question.answers[answerIndex].id}`;
});
}
console.log(matchIDs(pages, storedOBJ));
console.log(matchIDsAlt(pages[0].questions, storedOBJ));
I have an array
[
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 20 },
{ price: 20 },
]
and I want it transformed into
[
{ numElements: 4, price: 10 },
{ numElements: 2, price: 20 },
]
I have tried using arr.reduce((prev, curr) => ..., []) to accomplish this, but I can't figure out how to do it.
A traditional method might use a for/loop to wrangle the data, but these days JavaScript has a number of functional methods that can help. This code uses reduce and map. To get your data in the format you want is a two stage process.
First, use reduce to create a hash table using the price as a key (because you know the each price is going to be unique:
const obj = arr.reduce((p, c) => {
// If price exists as a key its value by 1
// otherwise set it to 1.
p[c.price] = ++p[c.price] || 1;
return p;
}, {});
OUTPUT
{
"10": 4,
"20": 2
}
As it stands you've got a perfectly good object that you can access by the key/price and I would probably just stop there:
obj['10'] // 4
But if you want to get that data into the format in your question, map over the object keys to return an array of new objects.
const out = Object.keys(obj).map(key => {
return { price: +key, numElements: obj[key] };
});
DEMO
var hash = {}, result = [];
arr.forEach(function(el){
if(hash[el.price]){
hash[el.price].numElements++;
}else{
result.push(hash[el.price]={price:el.price,numElements:1});
}
});
Run
May use a hash table for price lookup. Or with reduce and find:
arr.reduce((res,{price})=>
(( res.find(el=>el.price===price) || res[res.push({price,numElements:0})-1] )
.numElements++,res)
);
Run
You can use try this:
let arr = [
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 20 },
{ price: 20 },
]
let result = []
let counter = {}
arr.forEach( el => {
if (!counter[el.price]) counter[el.price] = 1
else counter[el.price]++
console.log(counter[el.price])
})
for (let id in counter) {
result.push({numElements: counter[id], price: id})
}
Assuming that the data comes sorted on price property, with a single .reduce() you may do as follows;
var data = [{ price: 10 }, { price: 10 }, { price: 10 }, { price: 10 }, { price: 20 }, { price: 20 }],
result = data.reduce((r,d,i) => i ? r[r.length-1].price === d.price ? (r[r.length-1].numElemenets++, r)
: (r.push(Object.assign({}, d, {numElemenets: 1})),r)
: [Object.assign({}, d, {numElemenets: 1})], {});
console.log(result);
You could look up the price in the result array and if not found insert a new object.
var data = [{ price: 10 }, { price: 10 }, { price: 10 }, { price: 10 }, { price: 20 }, { price: 20 }],
grouped = data.reduce((r, { price }) => {
var t = r.find(p => price === p.price);
t || r.push(t = { numElements: 0, price });
t.numElements++;
return r;
}, []);
console.log(grouped);
I am trying to add the unique values for the fiddle below in the output I want
{ category: 'fos', value: 70 },
{ category: 'nyedva', value: 30 }
I am able to get the unique values in the array not sure where to add the values
http://jsfiddle.net/mj3q0sk3/
var catalog={
products : [
{ category: 'fos', value: 10 },
{ category: 'fos', value: 20 },
{ category: 'nyedva', value: 30 },
{ category: 'fos', value: 40 },
]
};
var categories = [];
var sum=[];
$.each(catalog.products, function(index, value) {
if ($.inArray(value.category, categories)==-1) {
categories.push(value.category);
}
else {
console.log("CAt Val:" +value.category);
var total=value.value;
sum.push(total);
}
});
console.log(categories);
console.log(sum);
You can use forEach() loop to return desired result.
var catalog = {"products":[{"category":"fos","value":10},{"category":"fos","value":20},{"category":"nyedva","value":30},{"category":"fos","value":40}]}
var result = [];
catalog.products.forEach(function(e) {
var c = e.category;
!this[c] ? (this[c] = e, result.push(this[c])) : this[c].value += e.value
}, {})
console.log(result)
You can do this without the need for jQuery:
var res = catalog.products.reduce(function(res, product) {
if (!res.hasOwnProperty(product.category)) {
res[product.category] = 0;
}
res[product.category] += product.value;
return res;
}, {});
console.log(res);
This yields:
{ fos: 70, nyedva: 30 }
If you want it as an array of categories:
console.log(Object.keys(res).map(function(key) { return { category: key, value: res[key] }; }));
This will give you:
[ { category: 'fos', value: 70 },
{ category: 'nyedva', value: 30 } ]