Returning an object with map.call - javascript

This works:
evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.container .title'), function(e) {
return e.querySelector('.title').textContent;
});
}
evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.container .title a'), function(e) {
return e.querySelector('.title a').href;
});
}
evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.container img'), function(e) {
return e.querySelector('img').src;
});
}
.. however, I want to return all results as a single object, but this doesn't work because I can't figure how to get the index:
evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.container'), function(e) {
var data = {};
data[index].title = e.querySelector('.title').textContent;
data[index].link = e.querySelector('.title').href;
data[index].image = e.querySelector('img').src;
// return all results in a single object with each containing title, link and image
return data;
});
}
This is the complete function:
new Nightmare(
.goto('https://www.example.com')
.insert('form[id="gbqf"] input[id="gbqfq"]', 'keyword here')
.click('form[id="gbqf"] button[id="gbqfb"]')
.wait('.sh-sr__shop-result-group')
.visible('.sh-sr__shop-result-group')
.evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.sh-sr__shop-result-group .psli'), function(data, e) {
data.push({
title: e.querySelector('.pslmain h3').textContent,
thumb: e.querySelector('.pslimg img').src,
link: e.querySelector('.pslmain h3 a').href,
price: e.querySelector('.pslline .price').textContent,
stars: e.querySelector('span._Ezj div').getAttribute('aria-label'),
ratings: e.querySelector('a.shop__secondary.sh-rt__product').textContent,
//features: e.querySelector('._Lqc .shop__secondary _MAj').innerHTML,
//description: e.querySelector('._Lqc').textContent
});
return data;
});
})
.end()
.then(function(result) {
console.log(result);
})
.catch(function(error) {
console.error('Search failed:', error);
});

I am not sure what shape you want your final object to be . but this seems as close to first implementation as possible .
function evaluate(containerName){
return Array.prototype.reduce.call(document.querySelectorAll(containerName), function(acc , e){
acc.push({
title: e.querySelector('.title').textContent,
link: e.querySelector('.title a').href,
image: e.querySelector('img').src
})
return acc
} , [] )
}
returns an array of objs corresponding to containers indexs .

You may iterate over document.querySelectorAll('.container') before the map and build an array with indexes, like:
var kvArray = [{index:0, value:document.querySelectorAll('.container')[0]}, {index:2, value:20}, {index:3, value: 30}];
var reformattedArray = kvArray.map(function(obj){
var rObj = {};
rObj[obj.key] = obj.valor;
return rObj;
});
And use your object key to access the rObj correspondent place.
Anyway from the docs, seems to be that you can get the index from the callback function. Please take a look to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map?v=control
var new_array = arr.map(callback[, thisArg])
Parameters
callback
Function that produces an element of the new Array, taking three arguments:
currentValue
The current element being processed in the array.
index
The index of the current element being processed in the array.
array
The array map was called upon.
thisArg
Optional. Value to use as this when executing callback.
Hope it helps.

Related

How to convert object key dot notation to object tree in javascript

I have a form and I want to send it to an ajax endpoint, for this I have this helper method:
$.fn.serializeFormToObject = function() {
//serialize to array
var data = $(this).serializeArray();
//add also disabled items
$(':disabled[name]', this)
.each(function() {
data.push({ name: this.name, value: $(this).val() });
});
//map to object
var obj = {};
data.map(function(x) { obj[x.name] = x.value; });
return obj;
};
The problem is that I have dot notation in some of the names of my form (using MVC strong typed model)
So I have this object as result:
Task.Addresses.Box:""
Task.Addresses.City:"Limal"
Task.Addresses.Country:"Belgique"
Task.Deadline:"1/10/2017 12:18:18 PM"
Task.TaskSourceId:"1"
And the result expected would be:
{ Task : { Addresses: { Box: "", City: "Limal", Country: "Belgique"}, Deadline: "1/10/2017 12:18:18 PM", TaskSourceId:"1" } }
I use the lodash library but after some hours I cannot find a way to do this expected result.
Can someone provide me a working javascript helper to give the expected nested object?
Edit:
For duplicated question, the other question does not ask about combined nested object together
After the answer of #ori-drori, This code is working as expected:
$.fn.serializeFormToObject = function() {
//serialize to array
var data = $(this).serializeArray();
//add also disabled items
$(':disabled[name]', this)
.each(function() {
data.push({ name: this.name, value: $(this).val() });
});
//map to object
var obj = {};
data.map(function (x) { obj[x.name] = x.value; });
var objNested = {};
_.forEach(obj, function (value, key) { _.set(objNested, key, value) });
return objNested;
};
Iterate the data using Array#forEach, and assign the value to the path (name) using _.set():
data.forEach(function(x) { _.set(obj, x.name, x.value) });
I don't have you're original data, so here is a demo using the key-values you provided.
var fields = {
"Task.Addresses.Box": "",
"Task.Addresses.City": "Limal",
"Task.Addresses.Country": "Belgique",
"Task.Deadline": "1/10/2017 12:18:18 PM",
"Task.TaskSourceId": "1"
};
var obj = {};
_.forEach(fields, function(value, key) {
_.set(obj, key, value);
});
console.log(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

JSLINQ groupBy error

I am trying to manipulate a json file, so I am trying JSLINQ, but I can't figure out why I hit an error at groupBy().
The website that led me to this code.
var query = JSLINQ(json);
var result = query.groupBy(function (i) { //HERE is where the error hits.
return i.CustomerName; //Attribute of json
})
.select(function (i) {
console.log(i);
return {
CustomerName: i.Key, data: i.elements //I read that I get groupBy result like this.
.select(function (j) {
x = j.x, y = j.y //x and y are attributes
})
}
}).toArray();
query.groupBy is not a function
Ask and ye shall receive young padawan ...
var result = jslinq(data)
.groupBy(function (i) { return i.CustomerName; })
.select(function(g) {
return {
key: g.key,
items: jslinq(g.elements).select(function(i) { return { x: i.x, y: i.y } }).items
};
})
.toList();
console.log(result);
....
Key differences between yours and mine ...
jslinq has been lowered in the version listed on github
element collections in the groups need to be wrapped in jslinq() too to be queried

Create a pipeline from Json to streams with transducers-js and most.js

I have this Amd module
define(function (require, exports, module) {
'use strict';
var t = require("transducers");
var most = require("most");
var groupby = function (prev, curr) {
var key = curr.type;
if (!prev[key]) {
prev[key] = [curr];
} else {
prev[key].push(curr);
}
return prev;
};
function category(kv) {
return {
type: kv[0],
label: kv[1][0].label,
counter: kv[1].length
}
}
function dom(cat) {
var el = document.createElement("li");
el.innerHTML = cat.label;
return el;
}
function append(prev, curr) {
prev.appendChild(curr);
return prev;
}
function createClick(prev, curr) {
return prev.merge(most.fromEvent("click", curr)
.map(function (e) {
return e.currentTarget.innerHTML;
})
)
}
var xf = t.comp(
t.map(category),
t.map(dom)
);
module.exports = {
main: function (data) {
var groups = t.reduce(groupby, {}, data);
var container = t.transduce(xf, append, document.querySelector("ul"), groups);
var streams = t.reduce(createClick, most.empty(), [].slice.call(container.querySelectorAll("li"), 0));
streams.forEach(function (e) {
console.log("click", e);
});
}
};
});
Main function takes a list of items, then groups them by 'type' property. After that it creates and appends < li > elements. Finally it creates a stream of clicks. I'm new in reactive programming and transducers.
But I was wondering if there would be a way to create a pipeline.
I trouble because groupby is a reduction and a can't compose it in transducer. I'm sure I'm missing something. Thanks
Try and separate your problem into things that can operate on the individual item vs on the collection and wait until last to reduce them. also check into the often missed "scan" operation which can save you from over aggressive reductions
In your example, you have 3 reducing possible operations listed:
- merge all click streams into one stream of events
- merge all dom into a single ul
- count
the can all be accomplished with scan, but the issue arrises in that you want to unique categories, but you also count the non unique ones. It's not clear from your example if that's actually a requirement though...
Since most already works similar to transducers, you don't really need them. For this example I'll stick with pure mostjs;
var most = require("most");
function gel(tag) {
return document.createElement(tag);
}
var cache = Object.create(null);
function main(dataArray) {
most.from(dataArray)
//only pass along unique items to represent categories
//optionally, also count...
.filter(function uniq(item) {
var category = item.type;
if (!(category in cache))
cache[category] = 0;
cache[category]++;
return cache[category] == 1;
})
//transform
.map(function makeLI(item) {
var li = gel("li");
li.innerHTML = item.label;
item.dom = li;
})
//attach click handler
.map(function (item) {
item.click = most
.fromEvent("click", item.dom)
.map(function(e) {
item.label;
});
return item;
})
// merge
.scan(function mergeIn(all, item) {
el.appendChild(item.dom);
clicks.merge(item.click);
}, { ul: gel("ul"), clicks: most.empty() })
//force stream init b creating a demand
.drain()
//most resolve stream into promises, but we could have just
//as easily used reduce here instead
.then(function onComplete(all) {
all.clicks.forEach(function(msg) {
console.log("click", e);
})
})
}
further variation are possible. for example if we wanted to add a sublist for each category item, we could attach a stream to the context object for each category and incrementally append to each child as we go

Waiting to initialize untill data is loaded asynchronously

I am trying to design a personal app which loads data asynchronously and then displays a grid according to the windows 8.1 store apps.
i'm running into the issue that my ui is trying to execute before my data is loaded.
my current code:
(function () {
"use strict";
var asyncInProgress = true;
var groupedItems;
var list;
var observable;
var matches = new WinJS.Binding.List();
var matchGroups = new WinJS.Binding.List();
var BattleGrounds = new WinJS.Binding.List();
list = getData();
initGroups(list);
function initGroups(l) {
var groupedItems = list.createGrouped(
function groupKeySelector(item) { return item.group.key; },
function groupDataSelector(item) { return item.group; }
);
}
WinJS.Namespace.define("Data", {
Observable: WinJS.Class.define(function () {
this.dispatch = function () {
this.dispatchEvent("dataReady");
}
}),
getObservable: getObservable,
items: groupedItems,
groups: groupedItems.groups,
getItemReference: getItemReference,
getItemsFromGroup: getItemsFromGroup,
resolveGroupReference: resolveGroupReference,
resolveItemReference: resolveItemReference,
updateData: updateData,
getAsyncStatus: getAsyncStatus
});
WinJS.Class.mix(Data.Observable, WinJS.Utilities.eventMixin);
WinJS.Class.mix(Data.Observable, WinJS.Utilities.createEventProperties("dataReady"));
// Provides support for event listeners.
function getObservable() {
observable = new Data.Observable();
return observable;
}
// Get a reference for an item, using the group key and item title as a
// unique reference to the item that can be easily serialized.
function getItemReference(item) {
return [item.group.key, item.title, item.backgroundImage];
}
// This function returns a WinJS.Binding.List containing only the items
// that belong to the provided group.
function getItemsFromGroup(group) {
return list.createFiltered(function (item) { return item.group.key === group.key; });
}
// Get the unique group corresponding to the provided group key.
function resolveGroupReference(key) {
return groupedItems.groups.getItemFromKey(key).data;
}
// Get a unique item from the provided string array, which should contain a
// group key and an item title.
function resolveItemReference(reference) {
for (var i = 0; i < groupedItems.length; i++) {
var item = groupedItems.getAt(i);
if (item.group.key === reference[0] && item.title === reference[1]) {
return item;
}
}
}
function updateData() {
asyncInProgress = true;
BattleGrounds.splice(0, matches.length);
BattleGrounds._currentKey = 0;
groupedItems = null;
list = getData();
initGroups(list);
}
function getAsyncStatus() {
return asyncInProgress;
}
function getData() {
var darkGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY3B0cPoPAANMAcOba1BlAAAAAElFTkSuQmCC";
var lightGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY7h4+cp/AAhpA3h+ANDKAAAAAElFTkSuQmCC";
var mediumGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY5g8dcZ/AAY/AsAlWFQ+AAAAAElFTkSuQmCC";
var url = 'https://api.guildwars2.com/v1/wvw/matches.json';
acquireSyndication(url).then(function (response) {
// Remove any invalid characters from JSONp response.
var fixedResponse = response.responseText.replace(/\\'/g, "'");
var jsonObj = JSON.parse(fixedResponse);
jsonObj.wvw_matches.forEach(function (battle) {
var anet_id = value.wvw_match_id;
// Create Group
var matchGroup = {
key: anet_id,
title: anet_id
};
matchGroups.push(matchGroup);
// Get Details
acquireSyndication("https://api.guildwars2.com/v1/wvw/match_details.json?match_id=" + anet_id).then(function (json) {
var fixedJson = json.responseText.replace(/\\'/g, "'");
var obj = JSON.parse(fixedJson);
fixedJson.maps.forEach(function (value) {
BattleGrounds.push({
group: matchGroup, key: matchGroup.title, title: value.type,
subtitle: value.type, map: "eb", description: "NA", content: "NA", "type": value.type,
"scores": value.scores, "objectives": value.objectives, "bonuses": value.bonuses, backgroundImage: lightGray
});
});
}, function (error) {
var x = error.getAllResponseHeaders();
var matchGroup = matchGroups[0];
for (var i = 0; i < matchGroups.length; i++) {
flickrPosts.push({
group: matchGroups[i], key: matchGroup.title, title: "Error loading",
subtitle: "Error", backgroundImage: lightGray, published: "N/A", description: "N/A"
});
}
asyncInProgress = false;
observable.dispatch();
});
});
}, function (error) {
var x = error.getAllResponseHeaders();
var matchGroup = matchGroups[0];
for (var i = 0; i < matchGroups.length; i++) {
flickrPosts.push({
group: matchGroups[i], key: matchGroup.title, title: "Error loading",
subtitle: "Error", backgroundImage: lightGray, published: "N/A", description: "N/A"
});
}
asyncInProgress = false;
observable.dispatch();
});
return BattleGrounds;
}
function acquireSyndication(url) {
return WinJS.xhr({
url: url,
headers: { "If-Modified-Since": "Mon, 27 Mar 1972 00:00:00 GMT" }
});
}
})();
This errors out on groups: groupedItems.groups. which says that groups is undefined.
i know this is because the data is still being processed.
How am i going to work around this?
i took a look at the promise object but the entire concept confuses me as i don't know enough about the infrastructure of a windows 8 app.
The core of your problem is in the getData() function - it is not returning your data because it uses asynchronous calls to get the data. The data is not yet available when it returns. It appears that that function makes several asynchronous calls to get data (using acquireSyndication()). When those asynchronous functions finish sometime in the future, you then put that data into matchGroups and then later into BattleGrounds after more calls to acquireSyndication().
What you're doing is quite messy so there isn't a simple fix. Conceptually, you need to process the BattleGrounds data from the completion handler of the asynchronous code and ALL code that uses it must continue from inside that completion handler, not after the getData() call. You cannot call getData() and use it like a synchronous function because it's asynchronous. This requires asynchronous programming techniques.
If you are doing multiple asynchronous calls and trying to carry out some action after all of them have completed (which I think is what you're doing), then you will need to code specifically for that condition too. You can either use promises or you can keep a counter of how many ajax calls there are and in each completion function, you increment the counter and see if this is the last one that just completed and, if so, then you can process all the data and continue executing the rest of your code.
I would also suggest that you don't use promises in one part of a function and then completion callbacks in the very next part. Use one of the other, not a mixture, to keep your code clean.

Update collection object using Underscore / Lo-dash

I have two collections of objects. I iterate trough collection A and I want when ObjectId from A matches ObjectId from B, to update that Object in collection B.
Here is what I got so far:
var exerciseIds = _(queryItems).pluck('ExerciseId').uniq().valueOf();
var item = { Exercise: null, ExerciseCategories: [] };
var exerciseAndCategories = [];
//this part works fine
_.forEach(exerciseIds, function(id) {
var temp = _.findWhere(queryItems, { 'ExerciseId': id });
item.Exercise = temp.Exercise;
exerciseAndCategories.push(item);
});
//this is problem
_.forEach(queryItems, function (i) {
_(exerciseAndCategories).where({ 'ExerciseId': i.ExerciseId }).tap(function (x) {
x.ExerciseCategories.push(i.ExerciseCategory);
}).valueOf();
});
EDIT
Link to a Fiddle
Give this a try:
var exerciseIds = _(queryItems).pluck('ExerciseId').uniq().valueOf();
var item = {
Exercise: null,
ExerciseCategories: []
};
var exerciseAndCategories = [];
//this part works fine
_.forEach(exerciseIds, function (id) {
var temp = _.findWhere(queryItems, {
'ExerciseId': id
});
var newItem = _.clone(item);
newItem.Exercise = temp.ExerciseId;
exerciseAndCategories.push(newItem);
});
//this is problem
_.forEach(queryItems, function (i) {
_(exerciseAndCategories).where({
'Exercise': i.ExerciseId
}).tap(function (x) {
return _.forEach(x, function(item) {
item.ExerciseCategories.push(i.ExerciseCategory);
});
}).valueOf();
});
// exerciseAndCategories = [{"Exercise":1,"ExerciseCategories":["biking","cardio"]},{"Exercise":2,"ExerciseCategories":["biking","cardio"]}]
Main problem was that tap returns the array, not each item, so you have to use _.forEach within that.
FIDDLE

Categories

Resources